Examples

Arduino Examples

Relative Mode

Example to output data reports in Relative Mode.

examples/relative_mode/relative_mode.ino
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/*
 * This example reads data from the Cirque trackpad in "relative mode" and prints the values.
 *
 * See documentation at https://cirquepinnacle.rtfd.io/
 */
#include <CirquePinnacle.h>

#define SS_PIN 2
#define DR_PIN 7

PinnacleTouchSPI trackpad(DR_PIN, SS_PIN);
// If using I2C, then use the following line (not the line above)
// PinnacleTouchI2C trackpad(DR_PIN);

// an object to hold data reported by the Cirque trackpad
RelativeReport data;

void setup() {
  Serial.begin(115200);
  while (!Serial) {
    // wait till Serial monitor is opened
  }
  if (!trackpad.begin()) {
    Serial.println(F("Cirque Pinnacle not responding!"));
    while (true) {
      // hold program in infinite loop
    }
  }
  Serial.println(F("CirquePinnacle/examples/relative_mode"));
  trackpad.setDataMode(PINNACLE_RELATIVE);
  trackpad.relativeModeConfig();  // uses default config
  Serial.println(F("Touch the trackpad to see the data."));
}

void loop() {
  if (trackpad.available()) {
    trackpad.read(&data);
    Serial.print(F("Left:"));
    Serial.print(data.buttons & 1);
    Serial.print(F(" Right:"));
    Serial.print(data.buttons & 2);
    Serial.print(F(" Middle:"));
    Serial.print(data.buttons & 4);
    Serial.print(F("\tX:"));
    Serial.print(data.x);
    Serial.print(F("\tY:"));
    Serial.print(data.y);
    Serial.print(F("\tScroll:"));
    Serial.println(data.scroll);
  }
}

Absolute Mode

Example to output data reports in Absolute Mode.

examples/absolute_mode/absolute_mode.ino
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
/*
 * This example reads data from the Cirque trackpad in "absolute mode" and prints the values.
 *
 * See documentation at https://cirquepinnacle.rtfd.io/
 */
#include <math.h>  // sqrt(), pow(), atan2()
#include <CirquePinnacle.h>

#define SS_PIN 2
#define DR_PIN 7

PinnacleTouchSPI trackpad(DR_PIN, SS_PIN);
// If using I2C, then use the following line (not the line above)
// PinnacleTouchI2C trackpad(DR_PIN);

// an object to hold data reported by the Cirque trackpad
AbsoluteReport data;

void setup() {
  Serial.begin(115200);
  while (!Serial) {
    // wait till Serial monitor is opened
  }
  if (!trackpad.begin()) {
    Serial.println(F("Cirque Pinnacle not responding!"));
    while (true) {
      // hold program in infinite loop
    }
  }
  Serial.println(F("CirquePinnacle/examples/absolute_mode"));
  trackpad.setDataMode(PINNACLE_ABSOLUTE);
  trackpad.absoluteModeConfig(1);  // set count of z-idle packets to 1
  Serial.println(F("\n*** Enter 'M' to measure and print raw data."));
  Serial.println(F("*** Enter 'T' to measure and print trigonometric calculations.\n"));
  Serial.println(F("Touch the trackpad to see the data."));
}

/*
Showing all the Serial output below will slow down the board's ability to
read() data from the trackpad in a timely manner (resulting in data loss).
Use this compile-time definition to switch between printing
raw data (false) or trigonometry data (true)
*/
bool onlyShowTrigVals = false;

#ifndef M_PI
#define PI M_PI
#else
#define PI 3.14159
#endif

void loop() {
  if (trackpad.available()) {
    trackpad.read(&data);

    // datasheet recommends clamping the axes value to reliable range
    if (data.z) {  // only clamp values if Z axis is not idle.

      data.x = data.x > 1920 ? 1920 : (data.x < 128 ? 128 : data.x);  // 128 <= x <= 1920
      data.y = data.y > 1472 ? 1472 : (data.y < 64 ? 64 : data.y);    //  64 <= y <= 1472
    }

    if (!onlyShowTrigVals) {
      // print raw data from the trackpad
      Serial.print(F("B1:"));
      Serial.print(data.buttons & 1);
      Serial.print(F(" B2:"));
      Serial.print(data.buttons & 2);
      Serial.print(F(" B3:"));
      Serial.print(data.buttons & 4);
      Serial.print(F("\tX:"));
      Serial.print(data.x);
      Serial.print(F("\tY:"));
      Serial.print(data.y);
      Serial.print(F("\tZ:"));
      Serial.println(data.z);
    } else {
      // print trigonometric data from the trackpad
      if (!data.z) {  // only compute angle and radius if touching (or near) the sensor
        Serial.println(F("Idling"));
      } else {
        // coordinates assume axes have been clamped to recommended ranges
        double coord_x = (int16_t)(data.x) - 960;
        double coord_y = (int16_t)(data.y) - 736;  // NOTE: y-axis is inverted by default
        double radius = sqrt(pow(coord_x, 2) + pow(coord_y, 2));
        // angle (in degrees) ranges (-180, 180]
        double angle = atan2(coord_y, coord_x) * 180 / PI;

        // This Serial output is meant to be compatible with the Arduino IDE Serial Plotter applet.
        Serial.print(F("angle:"));
        Serial.print(angle);
        Serial.print(F(",radius:"));
        Serial.println(radius);
      }
    }
  }  // end if trackpad.available()

  while (Serial.available()) {
    char input = Serial.read();
    if (input == 't' || input == 'T') {
      onlyShowTrigVals = true;
    } else if (input == 'm' || input == 'M') {
      onlyShowTrigVals = false;
    }
  }
}

AnyMeas Mode

Example to output data reports in AnyMeas Mode.

examples/anymeas_mode/anymeas_mode.ino
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
/*
 * This example reads data from the Cirque trackpad in "anymeas mode" and prints the values.
 *
 * See documentation at https://cirquepinnacle.rtfd.io/
 */
#include <CirquePinnacle.h>

#define SS_PIN 2
#define DR_PIN 7

PinnacleTouchSPI trackpad(DR_PIN, SS_PIN);
// If using I2C, then use the following line (not the line above)
// PinnacleTouchI2C trackpad(DR_PIN);

typedef struct _MeasureVector {
  uint32_t toggle;
  uint32_t polarity;
} measureVector;

measureVector vectorDeterminants[] = {
  //  toggle  ,   polarity
  { 0x0000FFFF, 0x00000000 },  // toggle all x-axis bits negatively (to 0)
  { 0x0FFF0000, 0x00000000 },  // toggle all y-axis bits negatively (to 0)
  { 0x00FF00FF, 0x000000FF },  // toggle Y0-Y7 negatively (to 0) & X0-X7 positively (to 1)
  { 0x00FF00FF, 0x00FF0000 }   // toggle Y0-Y7 positively (to 1) & X0-X7 negatively (to 0)
};

const uint8_t variousVectors_size = sizeof(vectorDeterminants) / sizeof(measureVector);
int16_t compensations[variousVectors_size];

void compensate() {
  int16_t value;
  int32_t accumulatedValue;
  for (uint8_t i = 0; i < variousVectors_size; ++i) {
    uint8_t sweep = 0;
    accumulatedValue = 0;
    while (sweep < 5)  // take 5 measurements and average them for a bit lower noise compensation value
    {
      value = trackpad.measureAdc(vectorDeterminants[i].toggle, vectorDeterminants[i].polarity);
      sweep++;
      accumulatedValue += value;
    }
    compensations[i] = accumulatedValue / 5;
    Serial.print(F("compensate "));
    Serial.print(i);
    Serial.print(F(": "));
    Serial.println(compensations[i]);
  }
}

void setup() {
  Serial.begin(115200);
  while (!Serial) {
    // wait till Serial monitor is opened
  }
  if (!trackpad.begin()) {
    Serial.println(F("Cirque Pinnacle not responding!"));
    while (true) {
      // hold program in infinite loop
    }
  }
  Serial.println(F("CirquePinnacle/examples/anymeas_mode"));
  trackpad.setDataMode(PINNACLE_ANYMEAS);
  trackpad.anymeasModeConfig();
  compensate();
  Serial.println(F("starting in 5 seconds..."));
  delay(5000);
}

void loop() {
  for (uint8_t i = 0; i < variousVectors_size; i++) {
    int16_t measurement = trackpad.measureAdc(vectorDeterminants[i].toggle,
                                              vectorDeterminants[i].polarity);
    measurement -= compensations[i];
    Serial.print(F("meas"));
    Serial.print(i);
    Serial.print(F(":"));
    Serial.print(measurement);
    Serial.print(F("  \t"));
  }
  Serial.println();
}

Linux Examples

Relative Mode

Example to output data reports in Relative Mode.

examples/linux/relative_mode.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/*
 * This example reads data from the Cirque trackpad in "relative mode" and prints the values.
 *
 * See documentation at https://cirquepinnacle.rtfd.io/
 */
#include <iostream>                        // cout, endl
#include <CirquePinnacle/CirquePinnacle.h> // trackpad object

#ifdef USE_SW_DR // if using PINNACLE_SW_DR
    #define DR_PIN PINNACLE_SW_DR
#endif
#if defined(PINNACLE_DRIVER_mraa) && !defined(DR_PIN)
    #define DR_PIN 22 // GPIO25
#elif !defined(DR_PIN)
    #define DR_PIN 25
#endif
#define SS_PIN 0

#ifndef USE_I2C
PinnacleTouchSPI trackpad(DR_PIN, SS_PIN);
#else // If using I2C, then use the following line (not the line above)
PinnacleTouchI2C trackpad(DR_PIN);
#endif

// an object to hold data reported by the Cirque trackpad
RelativeReport data;

bool setup()
{
    if (!trackpad.begin()) {
        std::cout << "Cirque Pinnacle not responding!" << std::endl;
        return false;
    }
    std::cout << "CirquePinnacle/examples/linux/relative_mode\n"
              << std::endl;
#ifndef USE_SW_DR // if using PINNACLE_SW_DR
    std::cout << "-- Using HW DataReady pin." << std::endl;
#endif
#ifndef USE_I2C
    std::cout << "-- Using SPI interface." << std::endl;
#else
    std::cout << "-- Using I2C interface." << std::endl;
#endif
    std::cout << "\nTouch the trackpad to see the data" << std::endl;
    return true;
}

void loop()
{
    if (trackpad.available()) {
        trackpad.read(&data);
        std::cout << "Left:" << (unsigned int)(data.buttons & 1)
                  << " Right:" << (unsigned int)(data.buttons & 2)
                  << " Middle:" << (unsigned int)(data.buttons & 4)
                  << "\tX:" << (int)(data.x)
                  << "\tY:" << (int)(data.y)
                  << "\tScroll:" << (int)(data.scroll) << std::endl;
    }
}

int main()
{
    if (!setup()) { // if trackpad.begin() failed
        return -1;  // fail fast
    }
    while (true) { // use ctrl+C to exit
        loop();
    }
    return 0; // we will never reach this
}

Absolute Mode

Example to output data reports in Absolute Mode.

examples/linux/absolute_mode.cpp
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
/*
 * This example reads data from the Cirque trackpad in "absolute mode" and prints the values.
 *
 * See documentation at https://cirquepinnacle.rtfd.io/
 */
#include <cmath>                           // sqrt(), pow(), atan2(), M_PI
#include <iostream>                        // cout, endl, cin
#include <iomanip>                         // setprecision()
#include <CirquePinnacle/CirquePinnacle.h> // trackpad object, absoluteClampAxis()

#ifdef USE_SW_DR // if using PINNACLE_SW_DR
    #define DR_PIN PINNACLE_SW_DR
#endif
#if defined(PINNACLE_DRIVER_mraa) && !defined(DR_PIN)
    #define DR_PIN 22 // GPIO25
#elif !defined(DR_PIN)
    #define DR_PIN 25
#endif
#define SS_PIN 0

#ifndef USE_I2C
PinnacleTouchSPI trackpad(DR_PIN, SS_PIN);
#else // If using I2C, then use the following line (not the line above)
PinnacleTouchI2C trackpad(DR_PIN);
#endif

// an object to hold data reported by the Cirque trackpad
AbsoluteReport data;

/*
Showing all the printed output below will slow down the board's ability to
read() data from the trackpad in a timely manner (resulting in data loss).
Use this compile-time definition to switch between printing
raw data (false) or trigonometry data (true)
*/
static bool onlyShowTrigVals;

bool setup()
{
    if (!trackpad.begin()) {
        std::cout << "Cirque Pinnacle not responding!" << std::endl;
        return false;
    }
    std::cout << "CirquePinnacle/examples/linux/absolute_mode\n"
              << std::endl;
    trackpad.setDataMode(PINNACLE_ABSOLUTE);
    trackpad.absoluteModeConfig(1); // set count of z-idle packets to 1
#ifndef USE_SW_DR                   // if using PINNACLE_SW_DR
    std::cout << "-- Using HW DataReady pin." << std::endl;
#endif
#ifndef USE_I2C
    std::cout << "-- Using SPI interface." << std::endl;
#else
    std::cout << "-- Using I2C interface." << std::endl;
#endif
    std::cout << "\nShow trigonometric calculations? [y/N] ('N' means show raw data) ";
    char input;
    std::cin >> input;
    onlyShowTrigVals = input == 'y' || input == 'Y';
    std::cout << "showing ";
    if (onlyShowTrigVals)
        std::cout << "trigonometric calculations." << std::endl;
    else
        std::cout << "raw data." << std::endl;
    std::cout << "\nTouch the trackpad to see the data" << std::endl;
    return true;
}

void loop()
{
    if (trackpad.available()) {
        trackpad.read(&data);

        // datasheet recommends clamping the axes value to reliable range
        if (data.z) { // only clamp values if Z axis is not idle.

            data.x = data.x > 1920 ? 1920 : (data.x < 128 ? 128 : data.x); // 128 <= x <= 1920
            data.y = data.y > 1472 ? 1472 : (data.y < 64 ? 64 : data.y);   //  64 <= y <= 1472
        }

        if (!onlyShowTrigVals) {
            // print raw data from the trackpad
            std::cout << "B1: " << (unsigned int)(data.buttons & 1)
                      << " B2: " << (unsigned int)(data.buttons & 2)
                      << " B3: " << (unsigned int)(data.buttons & 4)
                      << "\tX: " << data.x
                      << "\tY: " << data.y
                      << "\tZ: " << (unsigned int)(data.z) << std::endl;
        }
        else {
            // print trigonometric data from the trackpad
            if (!data.z) { // only compute angle and radius if touching (or near) the sensor
                std::cout << "Idling" << std::endl;
            }
            else {
                // coordinates assume axes have been clamped to recommended ranges
                double coord_x = (int16_t)(data.x) - 960;
                double coord_y = (int16_t)(data.y) - 736; // NOTE: y-axis is inverted by default

                double radius = sqrt(pow(coord_x, 2) + pow(coord_y, 2));
                double angle = atan2(coord_y, coord_x) * 180 / M_PI; // angle (in degrees) ranges (-180, 180]

                std::cout << std::setprecision(5) << "angle: " << angle << "\tradius: " << radius << std::endl;
            }
        }
    } // end if trackpad.available()
} //end loop()

int main()
{
    if (!setup()) { // if trackpad.begin() failed
        return -1;  // fail fast
    }
    while (true) { // use ctrl+C to exit
        loop();
    }
    return 0; // we will never reach this
}

AnyMeas Mode

Example to output data reports in AnyMeas Mode.

examples/linux/anymeas_mode.cpp
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
/*
 * This example reads data from the Cirque trackpad in "anymeas mode" and prints the values.
 *
 * See documentation at https://cirquepinnacle.rtfd.io/
 */
#include <iostream>                        // cout, endl
#include <unistd.h>                        // sleep()
#include <CirquePinnacle/CirquePinnacle.h> // trackpad object

#ifdef PINNACLE_DRIVER_mraa
    #define DR_PIN 22 // GPIO25
#else
    #define DR_PIN 25
#endif
#define SS_PIN 0

#ifndef USE_I2C
PinnacleTouchSPI trackpad(DR_PIN, SS_PIN);
#else // If using I2C, then use the following line (not the line above)
PinnacleTouchI2C trackpad(DR_PIN);
#endif

typedef struct _MeasureVector
{
    unsigned long toggle;
    unsigned long polarity;
} measureVector;

measureVector vectorDeterminants[] = {
    // toggle  ,   polarity
    {0x0000FFFF, 0x00000000}, // toggle all x-axis bits negatively (to 0)
    {0x0FFF0000, 0x00000000}, // toggle all y-axis bits negatively (to 0)
    {0x00FF00FF, 0x000000FF}, // toggle Y0-Y7 negatively (to 0) & X0-X7 positively (to 1)
    {0x00FF00FF, 0x00FF0000}  // toggle Y0-Y7 positively (to 1) & X0-X7 negatively (to 0)
};

const uint8_t variousVectors_size = sizeof(vectorDeterminants) / sizeof(measureVector);
int16_t compensations[variousVectors_size];

void compensate()
{
    signed long accumulatedValue;
    for (uint8_t i = 0; i < variousVectors_size; ++i) {
        uint8_t sweep = 0;
        accumulatedValue = 0;
        while (sweep < 5) // take 5 measurements and average them for a bit lower noise compensation value
        {
            int16_t value = trackpad.measureAdc(vectorDeterminants[i].toggle, vectorDeterminants[i].polarity);
            sweep++;
            accumulatedValue += value;
        }
        compensations[i] = accumulatedValue / 5;
        std::cout << "compensation " << (unsigned int)i << ": " << compensations[i] << std::endl;
    }
}

bool setup()
{
    if (!trackpad.begin()) {
        std::cout << "Cirque Pinnacle not responding!" << std::endl;
        return false;
    }
    std::cout << "CirquePinnacle/examples/linux/anymeas_mode" << std::endl;
    trackpad.setDataMode(PINNACLE_ANYMEAS);
    trackpad.anymeasModeConfig();
#ifndef USE_SW_DR // if using PINNACLE_SW_DR
    std::cout << "-- Using HW DataReady pin." << std::endl;
#endif
#ifndef USE_I2C
    std::cout << "-- Using SPI interface." << std::endl;
#else
    std::cout << "-- Using I2C interface." << std::endl;
#endif
    compensate();

    for (uint8_t i = 5; i; --i) {
        std::cout << "starting in " << (unsigned int)i << "second" << (i < 1 ? 's' : ' ') << '\r';
        sleep(1);
    }
    std::cout << std::endl;
    return true;
}

void loop()
{
    for (unsigned int i = 0; i < variousVectors_size; i++) {
        int16_t measurement = trackpad.measureAdc(
            vectorDeterminants[i].toggle,
            vectorDeterminants[i].polarity);
        measurement -= compensations[i];
        std::cout << "meas" << i << ": " << measurement << "\t";
    }
    std::cout << std::endl;
}

int main()
{
    if (!setup()) { // if trackpad.begin() failed
        return -1;  // fail fast
    }
    while (true) { // use ctrl+C to exit
        loop();
    }
    return 0; // we will never reach this
}

RPi Pico SDK Examples

defaultPins.h

The DR_PIN AND SS_PIN example definitions for various RP2040 based boards.

examples/pico_sdk/defaultPins.h
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// pre-chosen pins for different boards
#ifndef DEFAULTPINS_H
#define DEFAULTPINS_H

#ifdef USE_SW_DR // if using PINNACLE_SW_DR
    #define DR_PIN PINNACLE_SW_DR
#endif

#if defined(ADAFRUIT_QTPY_RP2040)
    #define SS_PIN PICO_DEFAULT_UART_RX_PIN     // the pin labeled RX (GPIO5)
    #ifndef DR_PIN                              // if not using PINNACLE_SW_DR
        #define DR_PIN PICO_DEFAULT_UART_TX_PIN // the pin labeled TX (GPIO20)
    #endif

#elif defined(PIMORONI_TINY2040)
    #define SS_PIN PICO_DEFAULT_SPI_CSN_PIN // pin 5
    #ifndef DR_PIN                          // if not using PINNACLE_SW_DR
        #define DR_PIN 1                    // pin 1
    #endif

#elif defined(SPARFUN_THINGPLUS)
    #define SS_PIN 21     // the pin labeled 21
    #ifndef DR_PIN        // if not using PINNACLE_SW_DR
        #define DR_PIN 22 // the pin labeled 22
    #endif

#elif defined(ADAFRUIT_ITSYBITSY_RP2040)
    #define SS_PIN 12    // the pin labeled 2
    #ifndef DR_PIN       // if not using PINNACLE_SW_DR
        #define DR_PIN 6 // the pin labeled 7
    #endif

#else
    // pins available on (ADAFRUIT_FEATHER_RP2040 || Pico_board || Sparkfun_ProMicro || SparkFun MicroMod)

    #define SS_PIN 6
    #ifndef DR_PIN // if not using PINNACLE_SW_DR
        #define DR_PIN 8
    #endif
#endif // board detection macro defs

#endif // DEFAULTPINS_H

Relative Mode

Example to output data reports in Relative Mode.

examples/pico_sdk/relative_mode.cpp
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/*
 * This example reads data from the Cirque trackpad in "relative mode" and prints the values.
 *
 * See documentation at https://cirquepinnacle.rtfd.io/
 */
#include "pico/stdlib.h"    // printf(), getchar_timeout_us()
#include "pico/bootrom.h"   // reset_usb_boot()
#include <tusb.h>           // tud_cdc_connected()
#include <CirquePinnacle.h> // trackpad  object
#include "defaultPins.h"    // board presumptive default pin numbers for SS_PIN and DR_PIN

#ifndef USE_I2C
PinnacleTouchSPI trackpad(DR_PIN, SS_PIN);
#else // If using I2C, then use the following line (not the line above)
PinnacleTouchI2C trackpad(DR_PIN);
#endif

// an object to hold data reported by the Cirque trackpad
RelativeReport data;

bool setup()
{
    // wait here until the CDC ACM (serial port emulation) is connected
    while (!tud_cdc_connected()) {
        sleep_ms(10);
    }

    if (!trackpad.begin()) {
        printf("Cirque Pinnacle not responding!\n");
        return false;
    }
    printf("CirquePinnacle/examples/pico_sdk/relative_mode\n");

#ifndef USE_SW_DR
    printf("-- Using HW Data Ready pin\n");
#endif
#ifndef USE_I2C
    printf("-- Using SPI interface\n");
#else
    printf("-- Using I2C interface\n");
#endif
    printf("\n*** Enter 'B' to reset to bootloader.\n");
    printf("\nTouch the trackpad to see the data\n");
    return true;
}

void loop()
{
    if (trackpad.available()) {
        trackpad.read(&data);
        printf("Left:%d ", data.buttons & 1);
        printf("Right:%d ", data.buttons & 2);
        printf("Middle:%d", data.buttons & 4);
        printf("\tX: %d\tY: %d\tScroll: %d\n", data.x, data.y, data.scroll);
    }

    char input = getchar_timeout_us(0); // get char from buffer for user input
    if (input != PICO_ERROR_TIMEOUT && (input == 'b' || input == 'B')) {
        // reset to bootloader
        trackpad.shutdown(true);
        reset_usb_boot(0, 0);
    }
}

int main()
{
    stdio_init_all(); // init necessary IO for the RP2040

    while (!setup()) { // if trackpad.begin() failed
        // hold program in infinite attempts to initialize trackpad
    }
    while (true) {
        loop();
    }
    return 0; // we will never reach this
}

Absolute Mode

Example to output data reports in Absolute Mode.

examples/pico_sdk/absolute_mode.cpp
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/*
 * This example reads data from the Cirque trackpad in "absolute mode" and prints the values.
 *
 * See documentation at https://cirquepinnacle.rtfd.io/
 */
#include <cmath>            // sqrt(), pow(), atan2()
#include "pico/stdlib.h"    // printf(), getchar_timeout_us()
#include "pico/bootrom.h"   // reset_usb_boot()
#include <tusb.h>           // tud_cdc_connected()
#include <CirquePinnacle.h> // trackpad  object
#include "defaultPins.h"    // board presumptive default pin numbers for SS_PIN and DR_PIN

#ifndef USE_I2C
PinnacleTouchSPI trackpad(DR_PIN, SS_PIN);
#else // If using I2C, then use the following line (not the line above)
PinnacleTouchI2C trackpad(DR_PIN);
#endif

// an object to hold data reported by the Cirque trackpad
AbsoluteReport data;

#ifndef PI
    #define PI 3.14159
#endif

/*
Showing all the printed output below will slow down the board's ability to
read() data from the trackpad in a timely manner (resulting in data loss).
Change this variable (via Serial input) to switch between printing
raw data (false) or trigonometric data (true)
*/
bool onlyShowTrigVals = false;

bool setup()
{
    // wait here until the CDC ACM (serial port emulation) is connected
    while (!tud_cdc_connected()) {
        sleep_ms(10);
    }

    if (!trackpad.begin()) {
        printf("Cirque Pinnacle not responding!\n");
        return false;
    }
    printf("CirquePinnacle/examples/pico_sdk/absolute_mode\n");
    trackpad.setDataMode(PINNACLE_ABSOLUTE);
    trackpad.absoluteModeConfig(1); // set count of z-idle packets to 1

#ifndef USE_SW_DR
    printf("-- Using HW Data Ready pin\n");
#endif
#ifndef USE_I2C
    printf("-- Using SPI interface\n");
#else
    printf("-- Using I2C interface\n");
#endif
    printf("\n*** Enter 'M' to measure and print raw data.");
    printf("\n*** Enter 'T' to measure and print trigonometric calculations.");
    printf("\n*** Enter 'B' to reset to bootloader.\n");
    printf("\nTouch the trackpad to see the data\n");
    return true;
}

void loop()
{
    if (trackpad.available()) {
        trackpad.read(&data);

        // datasheet recommends clamping the axes value to reliable range
        if (data.z) { // only clamp values if Z axis is not idle.

            data.x = data.x > 1920 ? 1920 : (data.x < 128 ? 128 : data.x); // 128 <= x <= 1920
            data.y = data.y > 1472 ? 1472 : (data.y < 64 ? 64 : data.y);   //  64 <= y <= 1472
        }

        if (!onlyShowTrigVals) {
            // print raw data from the trackpad
            printf("B1:%d B2:%d B3:%d", data.buttons & 1, data.buttons & 2, data.buttons & 4);
            printf("\tX:%d\tY:%d\tZ:%d\n", data.x, data.y, data.z);
        }
        else {
            // print trigonometric calculations from the trackpad data
            if (!data.z) { // only compute angle and radius if touching (or near) the sensor
                printf("Idling\n");
            }
            else {
                // coordinates assume axes have been clamped to recommended ranges
                double coord_x = (int16_t)(data.x) - 960;
                double coord_y = (int16_t)(data.y) - 736; // NOTE: y-axis is inverted by default

                double radius = sqrt(pow(coord_x, 2) + pow(coord_y, 2));
                double angle = atan2(coord_y, coord_x) * 180 / PI; // angle (in degrees) ranges (-180, 180]

                printf("angle: %.2f\tradius: %.2f\n", angle, radius);
            }
        }
    } // end if trackpad.available()

    char input = getchar_timeout_us(0); // get char from buffer for user input
    if (input != PICO_ERROR_TIMEOUT && (input == 'b' || input == 'B')) {
        reset_usb_boot(0, 0); // reset to bootloader
    }
    else if (input != PICO_ERROR_TIMEOUT && (input == 'm' || input == 'M')) {
        onlyShowTrigVals = false;
    }
    else if (input != PICO_ERROR_TIMEOUT && (input == 't' || input == 'T')) {
        onlyShowTrigVals = true;
    }
} //end loop()

int main()
{
    stdio_init_all(); // init necessary IO for the RP2040

    while (!setup()) { // if trackpad.begin() failed
        // hold program in infinite attempts to initialize trackpad
    }
    while (true) {
        loop();
    }
    return 0; // we will never reach this
}

AnyMeas Mode

Example to output data reports in AnyMeas Mode.

examples/pico_sdk/anymeas_mode.cpp
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
/*
 * This example reads data from the Cirque trackpad in "anymeas mode" and prints the values.
 *
 * See documentation at https://cirquepinnacle.rtfd.io/
 */
#include "pico/stdlib.h"    // printf(), getchar_timeout_us()
#include "pico/bootrom.h"   // reset_usb_boot()
#include <tusb.h>           // tud_cdc_connected()
#include <CirquePinnacle.h> // trackpad  object
#include "defaultPins.h"    // board presumptive default pin numbers for SS_PIN and DR_PIN

#ifndef USE_I2C
PinnacleTouchSPI trackpad(DR_PIN, SS_PIN);
#else // If using I2C, then use the following line (not the line above)
PinnacleTouchI2C trackpad(DR_PIN);
#endif

typedef struct _MeasureVector
{
    unsigned long toggle;
    unsigned long polarity;
} measureVector;

measureVector vectorDeterminants[] = {
    // toggle  ,   polarity
    {0x0000FFFF, 0x00000000}, // toggle all x-axis bits negatively (to 0)
    {0x0FFF0000, 0x00000000}, // toggle all y-axis bits negatively (to 0)
    {0x00FF00FF, 0x000000FF}, // toggle Y0-Y7 negatively (to 0) & X0-X7 positively (to 1)
    {0x00FF00FF, 0x00FF0000}  // toggle Y0-Y7 positively (to 1) & X0-X7 negatively (to 0)
};

const uint8_t variousVectors_size = sizeof(vectorDeterminants) / sizeof(measureVector);
int16_t compensations[variousVectors_size];

void compensate()
{
    int32_t accumulatedValue;
    for (uint8_t i = 0; i < variousVectors_size; ++i) {
        uint8_t sweep = 0;
        accumulatedValue = 0;
        while (sweep < 5) // take 5 measurements and average them for a bit lower noise compensation value
        {
            int16_t value = trackpad.measureAdc(vectorDeterminants[i].toggle, vectorDeterminants[i].polarity);
            sweep++;
            accumulatedValue += value;
        }
        compensations[i] = accumulatedValue / 5;
        printf("compensation %d: %d\n", i, compensations[i]);
    }
}

bool setup()
{
    // wait here until the CDC ACM (serial port emulation) is connected
    while (!tud_cdc_connected()) {
        sleep_ms(10);
    }

    if (!trackpad.begin()) {
        printf("Cirque Pinnacle not responding!\n");
        return false;
    }
    printf("CirquePinnacle/examples/pico_sdk/anymeas_mode\n");
    trackpad.setDataMode(PINNACLE_ANYMEAS);

#ifndef USE_I2C
    printf("-- Using SPI interface\n");
#else
    printf("-- Using I2C interface\n");
#endif
    printf("\n*** Enter 'B' to reset to bootloader.\n");
    compensate();
    for (uint8_t i = 5; i; --i) {
        printf("starting in %d second%c\r", i, i > 1 ? 's' : ' ');
        sleep_ms(1000);
    }
    return true;
}

void loop()
{
    for (uint8_t i = 0; i < variousVectors_size; i++) {
        int16_t measurement = trackpad.measureAdc(
            vectorDeterminants[i].toggle,
            vectorDeterminants[i].polarity);
        measurement -= compensations[i];
        printf("meas%d: %d\t", i, measurement);
    }
    printf("\n");

    char input = getchar_timeout_us(0); // get char from buffer for user input
    if (input != PICO_ERROR_TIMEOUT && (input == 'b' || input == 'B')) {
        // reset to bootloader
        trackpad.shutdown(true);
        reset_usb_boot(0, 0);
    }
}

int main()
{
    stdio_init_all(); // init necessary IO for the RP2040

    while (!setup()) { // if trackpad.begin() failed
        // hold program in infinite attempts to initialize trackpad
    }
    while (true) {
        loop();
    }
    return 0; // we will never reach this
}

Python Examples

Relative Mode

Example to output data reports in Relative Mode.

examples/cpython/relative_mode.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
"""
This example reads data from the Cirque trackpad in "relative mode" and
prints the values.

See documentation at https://cirquepinnacle.rtfd.io/
"""

import time
from typing import Union
from cirque_pinnacle import (
    RelativeReport,
    PinnacleTouchSPI,
    PinnacleTouchI2C,
    PINNACLE_SW_DR,
    PINNACLE_DRIVER,
)

print("CirquePinnacle/examples/cpython/relative_mode\n")

# a HW ``dr_pin`` is more efficient, but not required for Absolute or Relative modes
dr_pin = PINNACLE_SW_DR  # uses internal DR flag
if not input("Use SW Data Ready? [y/N] ").lower().startswith("y"):
    print("-- Using HW Data Ready pin.")
    if PINNACLE_DRIVER == "mraa":
        dr_pin = 22  # GPIO25
    else:
        dr_pin = 25  # GPIO25

trackpad: Union[PinnacleTouchSPI, PinnacleTouchI2C]
if not input("Is the trackpad configured for I2C? [y/N] ").lower().startswith("y"):
    print("-- Using SPI interface.")
    ss_pin = 0  # uses /dev/spidev0.0 (CE0 or GPIO8)
    trackpad = PinnacleTouchSPI(dr_pin, ss_pin)
else:
    print("-- Using I2C interface")
    trackpad = PinnacleTouchI2C(dr_pin)

if not trackpad.begin():
    raise OSError("Cirque Pinnacle not responding!")

# an object to hold data reported by the Cirque trackpad
data = RelativeReport()


def print_data(timeout=6):
    """Print available data reports from the Pinnacle touch controller
    until there's no input for a period of ``timeout`` seconds."""
    print(
        "Touch the sensor to see the data. Exits after",
        timeout,
        "seconds of inactivity.",
    )
    start = time.monotonic()
    while time.monotonic() - start < timeout:
        while trackpad.available():  # is there new data?
            trackpad.read(data)
            print(data)
            start = time.monotonic()


def set_role():
    """Set the role using stdin stream. Arguments for functions can be
    specified using a space delimiter (e.g. 'M 10' calls `print_data(10)`)
    """
    user_input = (
        input(
            "\n*** Enter 'M' to measure and print data."
            "\n*** Enter 'Q' to quit example.\n"
        )
        or "?"
    ).split()
    if user_input[0].upper().startswith("M"):
        print_data(*[int(x) for x in user_input[1:2]])
        return True
    if user_input[0].upper().startswith("Q"):
        return False
    print(user_input[0], "is an unrecognized input. Please try again.")
    return set_role()


if __name__ == "__main__":
    try:
        while set_role():
            pass  # continue example until 'Q' is entered
    except KeyboardInterrupt:
        print(" Keyboard Interrupt detected.")
else:
    print("\nRun print_data() to read and print data.")

Absolute Mode

Example to output data reports in Absolute Mode.

examples/cpython/absolute_mode.py
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
"""
This example reads data from the Cirque trackpad in "absolute mode" and
prints the values.

See documentation at https://cirquepinnacle.rtfd.io/
"""

import math
import time
from typing import Union
from cirque_pinnacle import (
    AbsoluteReport,
    PinnacleTouchSPI,
    PinnacleTouchI2C,
    PINNACLE_SW_DR,
    PINNACLE_ABSOLUTE,
    PINNACLE_DRIVER,
)

print("CirquePinnacle/examples/cpython/absolute_mode")

# a HW ``dr_pin`` is more efficient, but not required for Absolute or Relative modes
dr_pin = PINNACLE_SW_DR  # uses internal DR flag
if not input("Use SW Data Ready? [y/N] ").lower().startswith("y"):
    print("-- Using HW Data Ready pin.")
    if PINNACLE_DRIVER == "mraa":
        dr_pin = 22  # GPIO25
    else:
        dr_pin = 25  # GPIO25

trackpad: Union[PinnacleTouchSPI, PinnacleTouchI2C]
if not input("Is the trackpad configured for I2C? [y/N] ").lower().startswith("y"):
    print("-- Using SPI interface.")
    ss_pin = 0  # uses /dev/spidev0.0 (CE0 or GPIO8)
    trackpad = PinnacleTouchSPI(dr_pin, ss_pin)
else:
    print("-- Using I2C interface")
    trackpad = PinnacleTouchI2C(dr_pin)

if not trackpad.begin():
    raise OSError("Cirque Pinnacle not responding!")

trackpad.data_mode = PINNACLE_ABSOLUTE
trackpad.absolute_mode_config(1)  # set count of z-idle packets to 1

# an object to hold data reported by the Cirque trackpad
data = AbsoluteReport()


def print_data(timeout=6):
    """Print available data reports from the Pinnacle touch controller
    until there's no input for a period of ``timeout`` seconds."""
    print(
        "Touch the sensor to see the data. Exits after",
        timeout,
        "seconds of inactivity.",
    )
    start = time.monotonic()
    while time.monotonic() - start < timeout:
        while trackpad.available():  # is there new data?
            trackpad.read(data)

            # specification sheet recommends clamping absolute position data of
            # X & Y axis for reliability
            if data.z:  # only clamp values if Z axis is not idle
                data.x = max(128, min(1920, data.x))  # 128 <= x < = 1920
                data.y = max(64, min(1472, data.y))  #   64 <= y < = 1472

            print(data)
            start = time.monotonic()


def print_trig(timeout=6):
    """Print available data reports from the Pinnacle touch controller as trigonometric
    calculations until there's no input for a period of ``timeout`` seconds."""
    print(
        "Touch the trackpad to see the data. Exits after",
        timeout,
        "seconds of inactivity.",
    )
    start = time.monotonic()
    while time.monotonic() - start < timeout:
        while trackpad.available():  # is there new data?
            trackpad.read(data)

            if not data.z:  # if not touching (or near) the sensor
                print("Idling")  # don't do calc when both axes are 0
            else:  # if touching (or near) the sensor
                # datasheet recommends clamping X & Y axis for reliability
                data.x = max(128, min(1920, data.x))  # 128 <= x <= 1920
                data.y = max(64, min(1472, data.y))  #   64 <= y <= 1472

                # coordinates assume axes have been clamped to recommended ranges
                coord_x = data.x - 960
                coord_y = data.y - 736  # NOTE: y-axis is inverted by default
                radius = math.sqrt(math.pow(coord_x, 2) + math.pow(coord_y, 2))
                # angle (in degrees) ranges [-180, 180];
                angle = math.atan2(coord_y, coord_x) * 180 / math.pi
                print("angle: %.2f\tradius: %.2f" % (angle, radius))
            start = time.monotonic()


def set_role():
    """Set the role using stdin stream. Arguments for functions can be
    specified using a space delimiter (e.g. 'M 10' calls `print_data(10)`)
    """
    user_input = (
        input(
            "\n*** Enter 'M' to measure and print raw data."
            "\n*** Enter 'T' to measure and print trigonometric calculations."
            "\n*** Enter 'Q' to quit example.\n"
        )
        or "?"
    ).split()
    if user_input[0].upper().startswith("M"):
        print_data(*[int(x) for x in user_input[1:2]])
        return True
    if user_input[0].upper().startswith("T"):
        print_trig(*[int(x) for x in user_input[1:2]])
        return True
    if user_input[0].upper().startswith("Q"):
        return False
    print(user_input[0], "is an unrecognized input. Please try again.")
    return set_role()


if __name__ == "__main__":
    try:
        while set_role():
            pass  # continue example until 'Q' is entered
    except KeyboardInterrupt:
        print(" Keyboard Interrupt detected.")
else:
    print(
        "\nRun print_data() to read and print raw data.",
        "Run print_trig() to measure and print trigonometric calculations.",
        sep="\n",
    )

AnyMeas Mode

Example to output data reports in AnyMeas Mode.

examples/cpython/anymeas_mode.py
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
"""
This example reads data from the Cirque trackpad in "anymeas mode" and
prints the values.

See documentation at https://cirquepinnacle.rtfd.io/
"""

import time
from typing import Union
from cirque_pinnacle import (
    PinnacleTouchSPI,
    PinnacleTouchI2C,  # noqa: imported but unused
    PINNACLE_ANYMEAS,
    PINNACLE_DRIVER,
)

print("CirquePinnacle/examples/cpython/anymeas_mode\n")

if PINNACLE_DRIVER == "mraa":
    dr_pin = 22  # GPIO25
else:
    dr_pin = 25  # GPIO25

trackpad: Union[PinnacleTouchSPI, PinnacleTouchI2C]
if not input("Is the trackpad configured for I2C? [y/N] ").lower().startswith("y"):
    print("-- Using SPI interface.")
    ss_pin = 0  # uses /dev/spidev0.0 (CE0 or GPIO8)
    trackpad = PinnacleTouchSPI(dr_pin, ss_pin)
else:
    print("-- Using I2C interface")
    trackpad = PinnacleTouchI2C(dr_pin)

if not trackpad.begin():
    raise OSError("Cirque Pinnacle not responding!")
trackpad.data_mode = PINNACLE_ANYMEAS

# we'll use this list of tuples as args to PinnacleTouch.measure_adc()
vectors = [
    #  toggle  ,   polarity
    (0x0000FFFF, 0x00000000),  # toggle all x-axis bits negatively (to 0)
    (0x0FFF0000, 0x00000000),  # toggle all y-axis bits negatively (to 0)
    (
        0x00FF00FF,
        0x000000FF,
    ),  # toggle Y0-Y7 negatively (to 0) & X0-X7 positively (to 1)
    (
        0x00FF00FF,
        0x00FF0000,
    ),  # toggle Y0-Y7 positively (to 1) & X0-X7 negatively (to 0)
]

# a list of compensations to use with measured `vectors`
compensation = [0] * len(vectors)


def compensate():
    sweep = 5
    for i, (toggle, polarity) in enumerate(vectors):
        value = 0
        for _ in range(sweep):
            value += trackpad.measure_adc(toggle, polarity)
        compensation[i] = int(value / sweep)
        print("compensation {}: {}".format(i, compensation[i]))


def take_measurements(timeout=6):
    """Read ``len(vectors)`` number of measurements and print results for
    ``timeout`` number of seconds."""
    print("Taking measurements for", timeout, "seconds.")
    start = time.monotonic()
    while time.monotonic() - start < timeout:
        for i, (toggle, polarity) in enumerate(vectors):
            result = trackpad.measure_adc(toggle, polarity)
            print("meas{}: {}".format(i, result - compensation[i]), end="\t")
        print()


def set_role():
    """Set the role using stdin stream. Arguments for functions can be
    specified using a space delimiter (e.g. 'C 10' calls `compensate(10)`)
    """
    user_input = (
        input(
            "\n*** Enter 'C' to get compensations for measurements."
            "\n*** Enter 'M' to read and print measurements."
            "\n*** Enter 'Q' to quit example.\n"
        )
        or "?"
    ).split()
    if user_input[0].upper().startswith("C"):
        compensate(*[int(x) for x in user_input[1:2]])
        return True
    if user_input[0].upper().startswith("M"):
        take_measurements(*[int(x) for x in user_input[1:2]])
        return True
    if user_input[0].upper().startswith("Q"):
        return False
    print(user_input[0], "is an unrecognized input. Please try again.")
    return set_role()


if __name__ == "__main__":
    try:
        while set_role():
            pass  # continue example until 'Q' is entered
    except KeyboardInterrupt:
        print(" Keyboard Interrupt detected.")
else:
    print(
        "\nRun compensate() to set compensations for measurements.",
        "Run take_measurements() to read and print measurements.",
        sep="\n",
    )