/* * Trill library for Arduino * (c) 2020 bela.io * * This library communicates with the Trill sensors * using I2C. * * BSD license */ #ifndef TRILL_H #define TRILL_H #if (ARDUINO >= 100) #include "Arduino.h" #else #include "WProgram.h" #endif #include "Wire.h" #define TRILL_SPEED_ULTRA_FAST 0 #define TRILL_SPEED_FAST 1 #define TRILL_SPEED_NORMAL 2 #define TRILL_SPEED_SLOW 3 class Touches { public: Touches() { num_touches = 0; }; typedef uint16_t TouchData_t; /* How many touches? */ uint8_t getNumTouches() const; /* Location and size of a particular touch, ranging from 0 to N-1. Returns -1 if no such touch exists. */ int touchLocation(uint8_t touch_num) const; int touchSize(uint8_t touch_num) const; void processCentroids(uint8_t maxCentroids); TouchData_t const* centroids; TouchData_t const* sizes; uint8_t num_touches;/* Number of touches. Updated by processCentroids() */ }; class Touches2D : public Touches { public: unsigned int getNumHorizontalTouches(); int touchHorizontalLocation(uint8_t touch_num); int touchHorizontalSize(uint8_t touch_num); protected: Touches2D() {}; Touches horizontal; }; class Trill : public Touches2D { public: Trill(); enum Mode { AUTO = -1, CENTROID = 0, RAW = 1, BASELINE = 2, DIFF = 3, NUM_MODES = 5 }; enum Device { TRILL_NONE = -1, TRILL_UNKNOWN = 0, TRILL_BAR = 1, TRILL_SQUARE = 2, TRILL_CRAFT = 3, TRILL_RING = 4, TRILL_HEX = 5, TRILL_FLEX = 6, TRILL_NUM_DEVICES = 7 }; struct TrillDefaults { Trill::Device device; Trill::Mode mode; uint8_t address; }; struct TrillDefaults trillDefaults[TRILL_NUM_DEVICES + 1] = { {TRILL_NONE, AUTO, 0xFF}, {TRILL_UNKNOWN, AUTO, 0xFF}, {TRILL_BAR, CENTROID, 0x20}, {TRILL_SQUARE, CENTROID, 0x28}, {TRILL_CRAFT, DIFF, 0x30}, {TRILL_RING, CENTROID, 0x38}, {TRILL_HEX, CENTROID, 0x40}, {TRILL_FLEX, DIFF, 0x48}, }; static constexpr uint8_t interCommandDelay = 15; /** * An array containing the valid values for the speed parameter * in setScanSettings() */ static constexpr uint8_t speedValues[4] = {0, 1, 2, 3}; /** * The maximum value for the setPrescaler() method */ static constexpr uint8_t prescalerMax = 8; /* Initialise the hardware */ int begin(Device device, uint8_t i2c_address = 255, TwoWire* wire = &Wire); /* Initialise the hardware, it's the same as begin() */ int setup(Device device, uint8_t i2c_address = 255, TwoWire* wire = &Wire) { return begin(device, i2c_address, wire); } /* --- Main communication --- */ /* Return the type of device attached, or 0 if none is attached. Same as begin(), but without re-initialising the system. */ int identify(); /** * Does the device have one axis of position sensing? * * @return `true` if the device has one axis of position sensing * and is set in #CENTROID mode, `false` * otherwise. */ bool is1D(); /** * Does the device have two axes of position sensing? * * @return `true` if the device has two axes of position sensing * and is set in #CENTROID mode, `false` * otherwise. */ bool is2D(); static Device probe(uint8_t i2c_address) { Trill t; /* Start I2C */ t.begin(Trill::TRILL_UNKNOWN, i2c_address); /* Check the type of device attached */ if(t.identify() != 0) { // Unable to identify device return Trill::TRILL_NONE; } return t.deviceType(); } /* Return the device type already identified */ Device deviceType() { return device_type_; }; /* Get the name of a given device */ static const char* getNameFromDevice(Device device); /* Return firmware version */ int firmwareVersion() { return firmware_version_; } /* Get the mode that the device is currently in */ Mode getMode() { return mode_; } /* Get the current address of the device */ uint8_t getAddress() { return i2c_address_; } /* Get the number of capacitive channels on the device */ unsigned int getNumChannels(); /* Return the number of "button" channels on the device */ unsigned int getNumButtons() { return 2 * (getMode() == CENTROID && TRILL_RING == deviceType());}; /* Read the latest scan value from the sensor. Returns true on success. */ boolean read(); /* Update the baseline value on the sensor */ void updateBaseline(); /* --- Data processing --- */ /* Button value for Ring? */ int getButtonValue(uint8_t button_num); /* --- Raw data handling --- */ /* Request raw data; wrappers for Wire */ boolean requestRawData(uint8_t max_length = 0xFF); int rawDataAvailable(); int rawDataRead(); /* --- Scan configuration settings --- */ void setMode(Mode mode); void setScanSettings(uint8_t speed, uint8_t num_bits); void setPrescaler(uint8_t prescaler); void setNoiseThreshold(uint8_t threshold); void setIDACValue(uint8_t value); void setMinimumTouchSize(uint16_t size); void setAutoScanInterval(uint16_t interval); private: void prepareForDataRead(); enum { kCommandNone = 0, kCommandMode = 1, kCommandScanSettings = 2, kCommandPrescaler = 3, kCommandNoiseThreshold = 4, kCommandIdac = 5, kCommandBaselineUpdate = 6, kCommandMinimumSize = 7, kCommandAutoScanInterval = 16, kCommandIdentify = 255 }; enum { kOffsetCommand = 0, kOffsetData = 4 }; enum { kMaxTouchNum1D = 5, kMaxTouchNum2D = 4 }; enum { kCentroidLengthDefault = 20, kCentroidLengthRing = 24, kCentroidLength2D = 32, kRawLength = 60 }; enum { kNumChannelsBar = 26, kNumChannelsRing = 30, kNumChannelsMax = 30 }; enum { kRawLengthBar = 52, kRawLengthHex = 60, kRawLengthRing = 56 }; TwoWire* wire_; uint8_t i2c_address_; /* Address of this slider on I2C bus */ Device device_type_; /* Which type of device is connected, if any */ uint8_t firmware_version_; /* Firmware version running on the device */ Mode mode_; /* Which mode the device is in */ uint8_t last_read_loc_; /* Which byte reads will begin from on the device */ uint8_t raw_bytes_left_; /* How many bytes still remaining to request? */ uint16_t buffer_[kCentroidLength2D * 2];/* Buffer for centroid response */ }; // first template argument is the max num of centroids // the second argument is the number of readings that will be processed at the // same time. This should be 0 if the data passed to process() is already ordered template <uint8_t _maxNumCentroids, uint8_t _numReadings> class CentroidDetection : public Touches { public: typedef uint16_t WORD; CentroidDetection() {}; CentroidDetection(const unsigned int* order); int begin(const uint8_t* order, unsigned int numReadings) { return setup(order, numReadings); } // pass nullptr if the data passed to process() is already ordered. int setup(const uint8_t* order, unsigned int orderLength) { this->order = order; Touches::centroids = this->centroids; Touches::sizes = this->sizes; num_touches = 0; this->orderLength = orderLength; if(orderLength > _numReadings) return -1; // cannot work with more than _numReadings return 0; } void process(const WORD* rawData) { uint8_t nMax = _numReadings < orderLength ? _numReadings : orderLength; if(order) { for(unsigned int n = 0; n < nMax; ++n) { data[n] = rawData[order[n]]; } cc.CSD_waSnsDiff = data; } else { // no reordering needed cc.CSD_waSnsDiff = rawData; } cc.calculateCentroids(centroids, sizes, _maxNumCentroids, 0, nMax, nMax); processCentroids(_maxNumCentroids); } void setMinimumTouchSize(TouchData_t minSize) { cc.wMinimumCentroidSize = minSize; } private: // a small helper class, whose main purpose is to wrap the #include // and make all the variables related to it private and multi-instance safe class CalculateCentroids { public: typedef uint8_t BYTE; WORD const * CSD_waSnsDiff; WORD wMinimumCentroidSize = 0; BYTE SLIDER_BITS = 7; WORD wAdjacentCentroidNoiseThreshold = 400; // Trough between peaks needed to identify two centroids //WORD calculateCentroids(WORD *centroidBuffer, WORD *sizeBuffer, BYTE maxNumCentroids, BYTE minSensor, BYTE maxSensor, BYTE numSensors); // calculateCentroids is defined here: #include "calculateCentroids.h" }; TouchData_t centroids[_maxNumCentroids]; TouchData_t sizes[_maxNumCentroids * 2]; const uint8_t* order; unsigned int orderLength; WORD data[_numReadings]; CalculateCentroids cc; }; class CustomSlider : public CentroidDetection<5, 30> {}; #endif /* TRILL_H */