├── .gitignore ├── library.properties ├── README.md ├── examples ├── craft-print │ └── craft-print.ino ├── hex-print │ └── hex-print.ino ├── bar-print │ └── bar-print.ino ├── flex-print-raw │ └── flex-print-raw.ino ├── trill-connect │ ├── trill-midi.pd │ └── trill-connect.ino ├── ring-print │ └── ring-print.ino ├── detect-all-devices │ └── detect-all-devices.ino ├── flex-print-slider │ └── flex-print-slider.ino ├── square-print │ └── square-print.ino ├── print-details │ └── print-details.ino ├── custom-slider │ └── custom-slider.ino ├── raw-multi-print │ └── raw-multi-print.ino └── craft-settings │ └── craft-settings.ino ├── LICENSE ├── calculateCentroids.h ├── Trill.h └── Trill.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | .*.sw? 3 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=Trill 2 | version=1.1.7 3 | author=Andrew McPherson , Adan Benito Temprano , Giulio Moro 4 | maintainer=Adan Benito Temprano 5 | sentence=A library for using the Trill family of capacitive sensors. 6 | paragraph= 7 | category=Sensors 8 | url=https://learn.bela.io/using-trill/trill-and-arduino/ 9 | architectures=* 10 | includes=Trill.h 11 | depends= 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Trill: Touch Sensing for Makers 2 | 3 | Trill sensors bring beautiful touch interaction to digital projects. Trill was [funded on Kickstarter in 2019](https://www.kickstarter.com/projects/423153472/trill-touch-sensing-for-makers), and Trill sensors are available now in the [Bela Shop](https://shop.bela.io/collections/trill). 4 | 5 | ## Trill Libraries and Examples 6 | 7 | The repository holds the Arduino library for Trill, as well as code examples. ([Bela and Linux library and examples are located here](https://github.com/BelaPlatform/Trill).) 8 | 9 | Visit [https://learn.bela.io/trill](https://learn.bela.io/trill) for full documentation and a Get Started guide. 10 | 11 | -------------------------------------------------------------------------------- /examples/craft-print/craft-print.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ____ _____ _ _ 3 | | __ )| ____| | / \ 4 | | _ \| _| | | / _ \ 5 | | |_) | |___| |___ / ___ \ 6 | |____/|_____|_____/_/ \_\ 7 | http://bela.io 8 | 9 | \example craft-print 10 | 11 | Trill Craft Print 12 | ================= 13 | 14 | This is an example of how to communicate with the Trill Craft 15 | sensor using the Trill Arduino library. 16 | 17 | The sensor is set to Differential mode and readings from each of the 18 | capacitive connections on the sensor ar eprinted to the serial port. 19 | */ 20 | 21 | #include 22 | 23 | Trill trillSensor; // for Trill Craft 24 | 25 | void setup() { 26 | // put your setup code here, to run once: 27 | Serial.begin(115200); 28 | int ret; 29 | while((ret = trillSensor.setup(Trill::TRILL_CRAFT))) { 30 | Serial.println("failed to initialise trillSensor"); 31 | Serial.print("Error code: "); 32 | Serial.println(ret); 33 | } 34 | } 35 | 36 | void loop() { 37 | // put your main code here, to run repeatedly: 38 | delay(100); 39 | trillSensor.requestRawData(); 40 | 41 | while(trillSensor.rawDataAvailable() > 0) { 42 | int data = trillSensor.rawDataRead(); 43 | if(data < 1000) 44 | Serial.print(0); 45 | if(data < 100) 46 | Serial.print(0); 47 | if(data < 10) 48 | Serial.print(0); 49 | Serial.print(data); 50 | Serial.print(" "); 51 | } 52 | Serial.println(""); 53 | } 54 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Augmented Instruments Ltd. 2 | 3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 4 | 5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 6 | 7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | 9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 10 | 11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 12 | -------------------------------------------------------------------------------- /examples/hex-print/hex-print.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ____ _____ _ _ 3 | | __ )| ____| | / \ 4 | | _ \| _| | | / _ \ 5 | | |_) | |___| |___ / ___ \ 6 | |____/|_____|_____/_/ \_\ 7 | http://bela.io 8 | 9 | \example hex-print 10 | 11 | Trill Hex Print 12 | =============== 13 | 14 | This is an example of how to communicate with the Trill Hex 15 | sensor using the Trill Arduino library. 16 | 17 | The sensor is set to Centroid mode and 2D touch location and size 18 | printed to the serial port. 19 | */ 20 | 21 | #include 22 | 23 | Trill trillSensor; 24 | boolean touchActive = false; 25 | 26 | void setup() { 27 | // Initialise serial and touch sensor 28 | Serial.begin(115200); 29 | int ret; 30 | while((ret = trillSensor.setup(Trill::TRILL_HEX))) { 31 | Serial.println("failed to initialise trillSensor"); 32 | Serial.print("Error code: "); 33 | Serial.println(ret); 34 | } 35 | } 36 | 37 | void loop() { 38 | // Read 20 times per second 39 | delay(50); 40 | trillSensor.read(); 41 | 42 | if(trillSensor.getNumTouches() > 0) { 43 | Serial.print(trillSensor.getNumTouches()); 44 | Serial.print(" "); 45 | Serial.print(trillSensor.getNumHorizontalTouches()); 46 | Serial.print(" "); 47 | 48 | for(int i = 0; i < trillSensor.getNumTouches(); i++) { 49 | Serial.print(trillSensor.touchLocation(i)); 50 | Serial.print(" "); 51 | Serial.print(trillSensor.touchSize(i)); 52 | Serial.print(" "); 53 | } 54 | for(int i = 0; i < trillSensor.getNumHorizontalTouches(); i++) { 55 | Serial.print(trillSensor.touchHorizontalLocation(i)); 56 | Serial.print(" "); 57 | Serial.print(trillSensor.touchHorizontalSize(i)); 58 | Serial.print(" "); 59 | } 60 | 61 | Serial.println(""); 62 | touchActive = true; 63 | } 64 | else if(touchActive) { 65 | // Print a single line when touch goes off 66 | Serial.println("0 0"); 67 | touchActive = false; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /examples/bar-print/bar-print.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ____ _____ _ _ 3 | | __ )| ____| | / \ 4 | | _ \| _| | | / _ \ 5 | | |_) | |___| |___ / ___ \ 6 | |____/|_____|_____/_/ \_\ 7 | http://bela.io 8 | 9 | \example bar-print 10 | 11 | Trill Bar Print 12 | =============== 13 | 14 | This is an example of how to communicate with the Trill Bar 15 | sensor using the Trill Arduino library. 16 | 17 | The sensor is set to Centroid mode and touch location and size 18 | printed to the serial port for each of the 5 different simultaneous 19 | touches. 20 | 21 | You can find our Processing library for visualising here: 22 | https://github.com/BelaPlatform/trill-processing-library/ 23 | The accompanying Processing sketch, `TrillBar.pde`, listens for 24 | touch information on the Arduino serial port* and displays it in a 25 | render of a Trill Bar. 26 | 27 | *NOTE: you may need to update the Processing port number (gPortNumber) 28 | to match that of your Arduino. 29 | */ 30 | 31 | #include 32 | 33 | Trill trillSensor; 34 | boolean touchActive = false; 35 | 36 | void setup() { 37 | // Initialise serial and touch sensor 38 | Serial.begin(115200); 39 | int ret; 40 | while((ret = trillSensor.setup(Trill::TRILL_BAR))) { 41 | Serial.println("failed to initialise trillSensor"); 42 | Serial.print("Error code: "); 43 | Serial.println(ret); 44 | } 45 | } 46 | 47 | void loop() { 48 | // Read 20 times per second 49 | delay(50); 50 | trillSensor.read(); 51 | 52 | if(trillSensor.getNumTouches() > 0) { 53 | for(int i = 0; i < trillSensor.getNumTouches(); i++) { 54 | Serial.print(trillSensor.touchLocation(i)); 55 | Serial.print(" "); 56 | Serial.print(trillSensor.touchSize(i)); 57 | Serial.print(" "); 58 | } 59 | Serial.println(""); 60 | touchActive = true; 61 | } 62 | else if(touchActive) { 63 | // Print a single line when touch goes off 64 | Serial.println("0 0"); 65 | touchActive = false; 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /examples/flex-print-raw/flex-print-raw.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ____ _____ _ _ 3 | | __ )| ____| | / \ 4 | | _ \| _| | | / _ \ 5 | | |_) | |___| |___ / ___ \ 6 | |____/|_____|_____/_/ \_\ 7 | http://bela.io 8 | 9 | \example flex-print-raw 10 | 11 | Trill Flex Print Slider 12 | ======================= 13 | 14 | This is an example of how to communicate with the Trill Flex 15 | sensor using the Trill Arduino library. 16 | 17 | The sensor is set to Differential mode and readings from each of the 18 | capacitive connections on the sensor are printed to the serial port. 19 | 20 | You can find our Processing library for visualising here: 21 | https://github.com/BelaPlatform/trill-processing-library/ 22 | The accompanying Processing sketch, `TrillFlexRaw.pde`, listens for 23 | touch information on the Arduino serial port* and displays it in a 24 | render of a each of the channels of Trill Flex. 25 | */ 26 | 27 | #include 28 | 29 | Trill trillSensor; 30 | 31 | void setup() { 32 | // put your setup code here, to run once: 33 | Serial.begin(115200); 34 | int ret; 35 | while((ret = trillSensor.setup(Trill::TRILL_FLEX))) { 36 | Serial.println("failed to initialise trillSensor"); 37 | Serial.print("Error code: "); 38 | Serial.println(ret); 39 | } 40 | // when the slider is connected we increase the 41 | // prescaler to deal with the increased baseline 42 | // capacitance it brings 43 | trillSensor.setPrescaler(3); 44 | delay(10); 45 | trillSensor.setNoiseThreshold(200); 46 | delay(10); 47 | // after any prescaler change, it's always good to update 48 | // the baseline, too. 49 | trillSensor.updateBaseline(); 50 | } 51 | 52 | void loop() { 53 | // put your main code here, to run repeatedly: 54 | delay(100); 55 | trillSensor.requestRawData(); 56 | 57 | while(trillSensor.rawDataAvailable() > 0) { 58 | int data = trillSensor.rawDataRead(); 59 | if(data < 1000) 60 | Serial.print(0); 61 | if(data < 100) 62 | Serial.print(0); 63 | if(data < 10) 64 | Serial.print(0); 65 | Serial.print(data); 66 | Serial.print(" "); 67 | } 68 | Serial.println(""); 69 | } 70 | -------------------------------------------------------------------------------- /examples/trill-connect/trill-midi.pd: -------------------------------------------------------------------------------- 1 | #N canvas 285 95 911 620 12; 2 | #X obj 57 147 << 7; 3 | #X obj 77 196 +; 4 | #X obj 78 247 / 16383; 5 | #X obj 78 271 hsl 162 19 0 1 0 0 empty empty empty -2 -10 0 12 #fcfcfc #000000 #000000 0 1; 6 | #X text 129 219 SQUARE X; 7 | #X text 289 217 SQUARE Y; 8 | #X text 300 457 BAR X; 9 | #X obj 257 147 << 7; 10 | #X obj 277 196 +; 11 | #X obj 278 247 / 16383; 12 | #X obj 278 271 hsl 162 19 0 1 0 0 empty empty empty -2 -10 0 12 #fcfcfc #000000 #000000 0 1; 13 | #X obj 257 387 << 7; 14 | #X obj 277 436 +; 15 | #X obj 278 487 / 16383; 16 | #X obj 278 511 hsl 162 19 0 1 0 0 empty empty empty -2 -10 0 12 #fcfcfc #000000 #000000 0 1; 17 | #X obj 457 147 << 7; 18 | #X obj 477 196 +; 19 | #X obj 478 247 / 16383; 20 | #X obj 478 271 hsl 162 19 0 1 0 0 empty empty empty -2 -10 0 12 #fcfcfc #000000 #000000 0 1; 21 | #X obj 447 387 << 7; 22 | #X obj 467 436 +; 23 | #X obj 468 487 / 16383; 24 | #X obj 468 511 hsl 162 19 0 1 0 0 empty empty empty -2 -10 0 12 #fcfcfc #000000 #000000 0 1; 25 | #X text 490 457 BAR SIZE; 26 | #X text 510 217 SQUARE SIZE; 27 | #X text 117 44 If using USB MIDI \, the data is sent as MSB + LSB control changes \, with the LSB CC being the base CC + 32; 28 | #X text 117 74 If using BLE MIDI \, only the MSB is sent.; 29 | #X text 118 -1 Use this patch in combination with the trill-mouse-midi example for Arduino \, ensuring you have enabled at least USB_MIDI or BLE_MIDI.; 30 | #X obj 57 99 ctlin 5 0; 31 | #X obj 95 125 ctlin 37 0; 32 | #X obj 257 99 ctlin 6 0; 33 | #X obj 295 125 ctlin 38 0; 34 | #X obj 457 99 ctlin 7 0; 35 | #X obj 495 125 ctlin 39 0; 36 | #X obj 257 339 ctlin 8 0; 37 | #X obj 295 365 ctlin 40 0; 38 | #X obj 447 339 ctlin 9 0; 39 | #X obj 485 365 ctlin 41 0; 40 | #X connect 0 0 1 0; 41 | #X connect 1 0 2 0; 42 | #X connect 2 0 3 0; 43 | #X connect 7 0 8 0; 44 | #X connect 8 0 9 0; 45 | #X connect 9 0 10 0; 46 | #X connect 11 0 12 0; 47 | #X connect 12 0 13 0; 48 | #X connect 13 0 14 0; 49 | #X connect 15 0 16 0; 50 | #X connect 16 0 17 0; 51 | #X connect 17 0 18 0; 52 | #X connect 19 0 20 0; 53 | #X connect 20 0 21 0; 54 | #X connect 21 0 22 0; 55 | #X connect 28 0 0 0; 56 | #X connect 29 0 1 1; 57 | #X connect 30 0 7 0; 58 | #X connect 31 0 8 1; 59 | #X connect 32 0 15 0; 60 | #X connect 33 0 16 1; 61 | #X connect 34 0 11 0; 62 | #X connect 35 0 12 1; 63 | #X connect 36 0 19 0; 64 | #X connect 37 0 20 1; 65 | -------------------------------------------------------------------------------- /examples/ring-print/ring-print.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ____ _____ _ _ 3 | | __ )| ____| | / \ 4 | | _ \| _| | | / _ \ 5 | | |_) | |___| |___ / ___ \ 6 | |____/|_____|_____/_/ \_\ 7 | http://bela.io 8 | 9 | \example ring-print 10 | 11 | Trill Ring Print 12 | ================ 13 | 14 | This is an example of how to communicate with the Trill Ring 15 | sensor using the Trill Arduino library. 16 | 17 | The sensor is set to Centroid mode and touch location and size 18 | printed to the serial port for each of the 5 different simultaneous 19 | touches (prepended by the character 'T'). 20 | 21 | The values of the 2 buttons on the back of the sensor are also printed 22 | to the serial port whenever their state changes with the following format: 23 | 'B buttonIndex buttonReading' 24 | */ 25 | 26 | #include 27 | 28 | Trill trillSensor; 29 | boolean touchActive = false; 30 | int prevButtonState[2] = { 0 , 0 }; 31 | 32 | void setup() { 33 | // Initialise serial and touch sensor 34 | Serial.begin(115200); 35 | int ret; 36 | while((ret = trillSensor.setup(Trill::TRILL_RING))) { 37 | Serial.println("failed to initialise trillSensor"); 38 | Serial.print("Error code: "); 39 | Serial.println(ret); 40 | } 41 | } 42 | 43 | void loop() { 44 | // Read 20 times per second 45 | delay(50); 46 | trillSensor.read(); 47 | 48 | if(trillSensor.getNumTouches() > 0) { 49 | Serial.print("T"); 50 | Serial.print(" "); 51 | for(int i = 0; i < trillSensor.getNumTouches(); i++) { 52 | Serial.print(trillSensor.touchLocation(i)); 53 | Serial.print(" "); 54 | Serial.print(trillSensor.touchSize(i)); 55 | Serial.print(" "); 56 | } 57 | Serial.println(""); 58 | touchActive = true; 59 | } 60 | else if(touchActive) { 61 | // Print a single line when touch goes off 62 | Serial.print("T"); 63 | Serial.print(" "); 64 | Serial.println("0 0"); 65 | touchActive = false; 66 | } 67 | 68 | for(int b = 0; b < trillSensor.getNumButtons(); b++) { 69 | int buttonState = trillSensor.getButtonValue(b); 70 | if(buttonState != prevButtonState[b]) { 71 | Serial.print("B"); 72 | Serial.print(" "); 73 | Serial.print(b); 74 | Serial.print(" "); 75 | Serial.print(buttonState); 76 | Serial.print(" "); 77 | Serial.println(""); 78 | prevButtonState[b] = buttonState; 79 | } 80 | } 81 | 82 | } 83 | -------------------------------------------------------------------------------- /examples/detect-all-devices/detect-all-devices.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ____ _____ _ _ 3 | | __ )| ____| | / \ 4 | | _ \| _| | | / _ \ 5 | | |_) | |___| |___ / ___ \ 6 | |____/|_____|_____/_/ \_\ 7 | http://bela.io 8 | 9 | \example detect-all-devices 10 | 11 | Detect All I2C Devices 12 | ====================== 13 | 14 | This example is a handy utility which will identify all connected 15 | I2C devices and print information on them to the console. 16 | 17 | When the program runs it will print the address and sensor type 18 | of all the Trill sensors you currently have connected to the I2C bus 19 | on the serial port. 20 | 21 | This is particularly useful if you are unsure of the address of the sensor after 22 | changing it via the solder bridges on the back. This example will also give 23 | you a total count of the amount of Trill sensors connected to Bela. 24 | 25 | NOTE: as this example scans several addresses on the i2c bus 26 | it could cause non-Trill peripherals connected to it to malfunction. 27 | */ 28 | 29 | #include 30 | 31 | void setup() { 32 | // Initialise serial 33 | Serial.begin(9600); 34 | Serial.println("Trill devices detected on I2C bus:"); 35 | Serial.println("Address | Type"); 36 | unsigned int total = 0; 37 | for(uint8_t n = 0x20; n <= 0x50; ++n) { 38 | Trill::Device device = Trill::probe(n); 39 | if(device != Trill::TRILL_NONE) { 40 | Serial.print('#'); 41 | Serial.print(n, HEX); 42 | Serial.print(" ("); 43 | Serial.print(n); 44 | Serial.print(")"); 45 | Serial.print(" "); 46 | printDeviceType((int)device); 47 | Serial.println(""); 48 | ++total; 49 | } 50 | } 51 | Serial.print("Total: "); 52 | Serial.println(total); 53 | } 54 | 55 | void printDeviceType(int deviceType) { 56 | switch(deviceType) { 57 | case Trill::TRILL_BAR: 58 | Serial.print("bar"); 59 | break; 60 | case Trill::TRILL_SQUARE: 61 | Serial.print("square"); 62 | break; 63 | case Trill::TRILL_HEX: 64 | Serial.print("hex"); 65 | break; 66 | case Trill::TRILL_RING: 67 | Serial.print("ring"); 68 | break; 69 | case Trill::TRILL_CRAFT: 70 | Serial.print("craft"); 71 | break; 72 | case Trill::TRILL_FLEX: 73 | Serial.print("flex"); 74 | break; 75 | case Trill::TRILL_UNKNOWN: 76 | Serial.print("unknown"); 77 | break; 78 | case Trill::TRILL_NONE: 79 | Serial.print("none"); 80 | break; 81 | } 82 | } 83 | 84 | void loop() { 85 | } 86 | -------------------------------------------------------------------------------- /examples/flex-print-slider/flex-print-slider.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ____ _____ _ _ 3 | | __ )| ____| | / \ 4 | | _ \| _| | | / _ \ 5 | | |_) | |___| |___ / ___ \ 6 | |____/|_____|_____/_/ \_\ 7 | http://bela.io 8 | 9 | \example flex-print-slider 10 | 11 | Trill Flex Print Slider 12 | ======================= 13 | 14 | This is an example of how to communicate with the Trill Flex 15 | sensor using the Trill Arduino library. 16 | 17 | The Trill Flex sensor is read in Differential mode by default. This is 18 | good for when you are working with custom designed Flex PCBs. 19 | See the `flex-print-raw` example to read each sensor channel independently. 20 | 21 | In this example we will use the sensor in Centroid mode to see 22 | touch location and size of touches on the default Flexible Bar sensor. 23 | Touchese will be printed to the serial port for each of the 5 different 24 | simultaneous touches. 25 | 26 | You can find our Processing library for visualising here: 27 | https://github.com/BelaPlatform/trill-processing-library/ 28 | The accompanying Processing sketch, `TrillFlexSlider.pde`, listens for 29 | touch information on the Arduino serial port* and displays it in a 30 | render of a Trill Flex. 31 | 32 | *NOTE: you may need to update the Processing port number (gPortNumber) 33 | to match that of your Arduino. 34 | */ 35 | 36 | #include 37 | 38 | Trill trillSensor; 39 | boolean touchActive = false; 40 | 41 | void setup() { 42 | // Initialise serial and touch sensor 43 | Serial.begin(115200); 44 | int ret; 45 | while((ret = trillSensor.setup(Trill::TRILL_FLEX))) { 46 | Serial.println("failed to initialise trillSensor"); 47 | Serial.print("Error code: "); 48 | Serial.println(ret); 49 | } 50 | // Set the sensor into Centroid mode 51 | // The default for Trill Flex is Differential mode 52 | trillSensor.setMode(Trill::CENTROID); 53 | delay(10); 54 | // when the slider is connected we increase the 55 | // prescaler to deal with the increased baseline 56 | // capacitance it brings 57 | trillSensor.setPrescaler(3); 58 | delay(10); 59 | trillSensor.setNoiseThreshold(200); 60 | delay(10); 61 | // after any prescaler change, it's always good to update 62 | // the baseline, too. 63 | trillSensor.updateBaseline(); 64 | } 65 | 66 | void loop() { 67 | // Read 20 times per second 68 | delay(50); 69 | trillSensor.read(); 70 | 71 | if(trillSensor.getNumTouches() > 0) { 72 | for(int i = 0; i < trillSensor.getNumTouches(); i++) { 73 | Serial.print(trillSensor.touchLocation(i)); 74 | Serial.print(" "); 75 | Serial.print(trillSensor.touchSize(i)); 76 | Serial.print(" "); 77 | } 78 | Serial.println(""); 79 | touchActive = true; 80 | } 81 | else if(touchActive) { 82 | // Print a single line when touch goes off 83 | Serial.println("0 0"); 84 | touchActive = false; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /examples/square-print/square-print.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ____ _____ _ _ 3 | | __ )| ____| | / \ 4 | | _ \| _| | | / _ \ 5 | | |_) | |___| |___ / ___ \ 6 | |____/|_____|_____/_/ \_\ 7 | http://bela.io 8 | 9 | \example square-print 10 | 11 | Trill Square Print 12 | ================== 13 | 14 | This is an example of how to communicate with the Trill Square 15 | sensor using the Trill Arduino library. 16 | 17 | The sensor is set to Centroid mode and 2D touch location and size 18 | printed to the serial port. 19 | 20 | You can find our Processing library for visualising here: 21 | https://github.com/BelaPlatform/trill-processing-library/ 22 | The accompanying Processing sketch, `TrillSquare.pde`, listens for 23 | touch information on the Arduino serial port and displays it in a 24 | render of a Trill Square. 25 | 26 | You may need to update the Processing port number (gPortNumber) 27 | to match that of your Arduino and set `verbose = false` below in order to 28 | make the serial data parseable by the Processing sketch. 29 | */ 30 | 31 | #include 32 | 33 | Trill trillSensor; 34 | boolean touchActive = false; 35 | const boolean verbose = true; // set this to false to communicate to the Processing GUI via UART. 36 | 37 | void setup() { 38 | // Initialise serial and touch sensor 39 | Serial.begin(115200); 40 | int ret; 41 | while((ret = trillSensor.setup(Trill::TRILL_SQUARE))) { 42 | Serial.println("failed to initialise trillSensor"); 43 | Serial.print("Error code: "); 44 | Serial.println(ret); 45 | } 46 | if(verbose) 47 | Serial.println("Success initialising trillSensor"); 48 | } 49 | 50 | void loop() { 51 | // Read 20 times per second 52 | delay(50); 53 | trillSensor.read(); 54 | 55 | if(trillSensor.getNumTouches() > 0 || trillSensor.getNumHorizontalTouches() > 0) { 56 | if(verbose) 57 | Serial.print("V["); 58 | Serial.print(trillSensor.getNumTouches()); 59 | if(verbose) { 60 | Serial.print("] "); 61 | Serial.print("H["); 62 | } 63 | else 64 | Serial.print(" "); 65 | Serial.print(trillSensor.getNumHorizontalTouches()); 66 | if(verbose) 67 | Serial.print("] "); 68 | else 69 | Serial.print(" "); 70 | 71 | for(int i = 0; i < trillSensor.getNumTouches(); i++) { 72 | Serial.print(trillSensor.touchLocation(i)); 73 | Serial.print(" "); 74 | Serial.print(trillSensor.touchSize(i)); 75 | Serial.print(" "); 76 | } 77 | for(int i = 0; i < trillSensor.getNumHorizontalTouches(); i++) { 78 | Serial.print(trillSensor.touchHorizontalLocation(i)); 79 | Serial.print(" "); 80 | Serial.print(trillSensor.touchHorizontalSize(i)); 81 | Serial.print(" "); 82 | } 83 | 84 | Serial.println(""); 85 | touchActive = true; 86 | } 87 | else if(touchActive) { 88 | // Print a single line when touch goes off 89 | if(verbose) 90 | Serial.println("V[0] H[0]"); 91 | else 92 | Serial.println("0 0"); 93 | touchActive = false; 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /examples/print-details/print-details.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ____ _____ _ _ 3 | | __ )| ____| | / \ 4 | | _ \| _| | | / _ \ 5 | | |_) | |___| |___ / ___ \ 6 | |____/|_____|_____/_/ \_\ 7 | http://bela.io 8 | 9 | \example print-details 10 | 11 | Trill Print Details 12 | =================== 13 | 14 | This is an example of how to communicate with the Trill Craft 15 | sensor using the Trill Arduino library. 16 | 17 | On setup(), all details from the sensor (including I2C address 18 | type, mode, number of capacitive contacts, etc) are printed to 19 | serial. 20 | 21 | Although this example works for Trill Craft and its default 22 | address out of the box, it can be used with any other Trill 23 | sensor by changing the parameters of the Trill.begin() 24 | method. 25 | */ 26 | 27 | #include 28 | 29 | Trill trill; 30 | boolean touchActive = false; 31 | 32 | void setup() { 33 | // Initialise serial 34 | Serial.begin(9600); 35 | 36 | // Initialise Trill sensor 37 | int ret; 38 | while((ret = trill.setup(Trill::TRILL_CRAFT))) { 39 | Serial.println("Failed to initialise Trill device"); 40 | Serial.print("Error code: "); 41 | Serial.println(ret); 42 | } 43 | 44 | Serial.println("Trill Device Details: "); 45 | int address = trill.getAddress(); 46 | Serial.print("\t- I2C address: "); 47 | Serial.print("#"); 48 | Serial.print(address, HEX); 49 | Serial.print(" ("); 50 | Serial.print(address); 51 | Serial.println(")"); 52 | 53 | int deviceType = trill.deviceType(); 54 | Serial.print("\t- Trill device type: "); 55 | switch(deviceType) { 56 | case Trill::TRILL_BAR: 57 | Serial.println("bar"); 58 | break; 59 | case Trill::TRILL_SQUARE: 60 | Serial.println("square"); 61 | break; 62 | case Trill::TRILL_HEX: 63 | Serial.println("hex"); 64 | break; 65 | case Trill::TRILL_RING: 66 | Serial.println("ring"); 67 | break; 68 | case Trill::TRILL_CRAFT: 69 | Serial.println("craft"); 70 | break; 71 | case Trill::TRILL_FLEX: 72 | Serial.print("flex"); 73 | break; 74 | case Trill::TRILL_UNKNOWN: 75 | Serial.println("unknown"); 76 | break; 77 | case Trill::TRILL_NONE: 78 | Serial.println("none"); 79 | break; 80 | } 81 | int firmwareRev = trill.firmwareVersion(); 82 | Serial.print("\t- Firmware version: "); 83 | Serial.println(firmwareRev); 84 | 85 | int mode = trill.getMode(); 86 | Serial.print("\t- Sensor mode: "); 87 | switch(mode) { 88 | case Trill::CENTROID: 89 | Serial.println("centroid"); 90 | break; 91 | case Trill::RAW: 92 | Serial.println("raw"); 93 | break; 94 | case Trill::BASELINE: 95 | Serial.println("baseline"); 96 | break; 97 | case Trill::DIFF: 98 | Serial.println("differential"); 99 | break; 100 | case Trill::AUTO: 101 | Serial.println("auto"); 102 | break; 103 | } 104 | 105 | Serial.print("\t- Number of available centroid dimensions: "); 106 | if(trill.is1D()) { 107 | Serial.println(1); 108 | } else if(trill.is2D()) { 109 | Serial.println(2); 110 | } else { 111 | Serial.println(0); 112 | } 113 | 114 | int numChannels = trill.getNumChannels(); 115 | Serial.print("\t- Number of capacitive channels: "); 116 | Serial.println(numChannels); 117 | 118 | int numButtons = trill.getNumButtons(); 119 | Serial.print("\t- Number of button channels: "); 120 | Serial.println(numButtons); 121 | Serial.println(); 122 | } 123 | 124 | void loop() { 125 | } 126 | -------------------------------------------------------------------------------- /examples/custom-slider/custom-slider.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ____ _____ _ _ 3 | | __ )| ____| | / \ 4 | | _ \| _| | | / _ \ 5 | | |_) | |___| |___ / ___ \ 6 | |____/|_____|_____/_/ \_\ 7 | http://bela.io 8 | 9 | This example allows you to create a slider from an arbitrary number of pads 10 | which can be in any order. This example uses a Trill Flex sensor with a 11 | custom flexible sensor with 3 sliders next to each other. See 12 | learn.bela.io/flex-tutorial/ for more info on the sensor. 13 | 14 | Trill Flex has 30 capactive pads in total and we are splitting this 15 | into 3 groups of 10 pads. The order of the pads and their pin numbering is 16 | defined in slider0Pads etc. We can also set the max number of centroid 17 | which will define how many touches can be registered per slider. This 18 | is currently set to 3 meaning that 3 individual touch points can be registered 19 | per sensor. 20 | 21 | Each touch has a location and a touch size which equates to how hard the finger 22 | is pushing on the sensor. This example is particularly useful for working with 23 | Trill Flex and Trill Craft. When working with these sensors it always important 24 | to check that the Prescaler and Noisethreshold settings are optimum for your 25 | application. Experiment with different values if you are not getting a reading 26 | or seeing lots of cross talk between the sensors. 27 | */ 28 | 29 | #include 30 | 31 | Trill trillSensor; 32 | 33 | const unsigned int NUM_TOTAL_PADS = 30; 34 | CustomSlider::WORD rawData[NUM_TOTAL_PADS]; 35 | 36 | const uint8_t slider0NumPads = 10; 37 | const uint8_t slider1NumPads = 10; 38 | const uint8_t slider2NumPads = 10; 39 | 40 | // Order of the pads used by each slider 41 | uint8_t slider0Pads[slider0NumPads] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0}; 42 | uint8_t slider1Pads[slider1NumPads] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19}; 43 | uint8_t slider2Pads[slider2NumPads] = {20, 21, 22, 23, 24, 25, 26, 27, 28, 29}; 44 | 45 | const unsigned int maxNumCentroids = 3; 46 | const unsigned int numSliders = 3; 47 | CustomSlider sliders[numSliders]; 48 | 49 | void setup() { 50 | sliders[0].setup(slider0Pads, slider0NumPads); 51 | sliders[1].setup(slider1Pads, slider1NumPads); 52 | sliders[2].setup(slider2Pads, slider2NumPads); 53 | // Initialise serial and touch sensor 54 | Serial.begin(115200); 55 | int ret; 56 | while((ret = trillSensor.setup(Trill::TRILL_FLEX))) { 57 | Serial.println("failed to initialise trillSensor"); 58 | Serial.println("Retrying..."); 59 | Serial.println("Error code"); 60 | Serial.println(ret); 61 | delay(100); 62 | } 63 | Serial.println("Success initialising trillSensor"); 64 | trillSensor.setMode(Trill::DIFF); 65 | // We recommend a prescaler value of 4 66 | trillSensor.setPrescaler(4); 67 | // Experiment with this value to avoid corss talk between sliders if they are position close together 68 | trillSensor.setNoiseThreshold(200); 69 | } 70 | 71 | void loop() { 72 | // Read 20 times per second 73 | delay(50); 74 | if(!trillSensor.requestRawData()) { 75 | Serial.println("Failed reading from device. Is it disconnected?"); 76 | return setup(); 77 | } 78 | unsigned n = 0; 79 | // read all the data from the device into a local buffer 80 | while(trillSensor.rawDataAvailable() > 0 && n < NUM_TOTAL_PADS) { 81 | rawData[n++] = trillSensor.rawDataRead(); 82 | } 83 | for(uint8_t n = 0; n < numSliders; ++n) { 84 | // have each custom slider process the raw data into touches 85 | sliders[n].process(rawData); 86 | Serial.print("| s"); 87 | Serial.print(n); 88 | Serial.print("["); 89 | Serial.print(sliders[n].getNumTouches()); 90 | Serial.print("]: "); 91 | if(sliders[n].getNumTouches() > 0) { 92 | for(int i = 0; i < sliders[n].getNumTouches(); i++) { 93 | Serial.print(sliders[n].touchLocation(i)); 94 | Serial.print(" "); 95 | Serial.print(sliders[n].touchSize(i)); 96 | Serial.print(" "); 97 | } 98 | } 99 | } 100 | Serial.println(""); 101 | } 102 | -------------------------------------------------------------------------------- /examples/raw-multi-print/raw-multi-print.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* 4 | ____ _____ _ _ 5 | | __ )| ____| | / \ 6 | | _ \| _| | | / _ \ 7 | | |_) | |___| |___ / ___ \ 8 | |____/|_____|_____/_/ \_\ 9 | http://bela.io 10 | 11 | \example raw-print-multi 12 | 13 | Trill Print Multiple 14 | ==================== 15 | 16 | This is a example of using multiple trill sensors with a single Arduino board. 17 | The I2C pins of all the sensors are connected together (SCL with SCL, SDA with SDA). 18 | Each sensor has a unique address. Here is the table of address for each sensor type, 19 | the first column is the default address: 20 | 21 | | Type: | Addresses (trillHex) | 22 | |--------|----------------------------------------------| 23 | | BAR | 0x20 0x21 0x22 0x23 0x24 0x25 0x26 0x27 0x28 | 24 | | SQUARE | 0x28 0x29 0x2A 0x2B 0x2C 0x2D 0x2E 0x2F 0x30 | 25 | | CRAFT | 0x30 0x31 0x32 0x33 0x34 0x35 0x36 0x37 0x38 | 26 | | RING | 0x38 0x39 0x3A 0x3B 0x3C 0x3D 0x3E 0x3F 0x40 | 27 | | HEX | 0x40 0x41 0x42 0x43 0x44 0x45 0x46 0x47 0x48 | 28 | | FLEX | 0x48 0x49 0x4A 0x4B 0x4C 0x4D 0x4E 0x4F 0x50 | 29 | 30 | In this example the sensor readings are printed to the console as raw values using the 31 | `rawDataRead()` method. 32 | */ 33 | 34 | Trill trillBar; // for Trill Bar 35 | Trill trillSquare; // for Trill Square 36 | Trill trillCraft; // for Trill Craft 37 | Trill trillRing; // for Trill Ring 38 | Trill trillHex; // for Trill Hex 39 | Trill trillFlex; // for Trill Flex 40 | 41 | 42 | void setup() { 43 | // put your setup code here, to run once: 44 | Serial.begin(115200); 45 | 46 | if(trillBar.setup(Trill::TRILL_BAR) != 0) 47 | Serial.println("failed to initialise trill bar"); 48 | 49 | if(trillSquare.setup(Trill::TRILL_SQUARE) != 0) 50 | Serial.println("failed to initialise trill square"); 51 | 52 | if(trillCraft.setup(Trill::TRILL_CRAFT) != 0) 53 | Serial.println("failed to initialise trill craft"); 54 | 55 | if(trillRing.setup(Trill::TRILL_RING) != 0) 56 | Serial.println("failed to initialise trill ring"); 57 | 58 | if(trillHex.setup(Trill::TRILL_HEX) != 0) 59 | Serial.println("failed to initialise trill hex"); 60 | 61 | if(trillFlex.setup(Trill::TRILL_FLEX) != 0) 62 | { 63 | Serial.println("failed to initialise trill flex"); 64 | } else { 65 | delay(10); 66 | trillFlex.setPrescaler(4); 67 | delay(10); 68 | trillFlex.updateBaseline(); 69 | delay(10); 70 | } 71 | 72 | trillBar.setMode(Trill::DIFF); 73 | trillSquare.setMode(Trill::DIFF); 74 | trillCraft.setMode(Trill::DIFF); 75 | trillRing.setMode(Trill::DIFF); 76 | trillHex.setMode(Trill::DIFF); 77 | trillFlex.setMode(Trill::DIFF); 78 | } 79 | 80 | void printLine(unsigned int n, char character = '_') { 81 | for(int i = 0; i < n; i++) { 82 | Serial.print(character); 83 | } 84 | Serial.println(""); 85 | } 86 | 87 | void printRawData(Trill & trill) { 88 | while(trill.rawDataAvailable() > 0) { 89 | int data = trill.rawDataRead(); 90 | if(data < 1000) 91 | Serial.print(0); 92 | if(data < 100) 93 | Serial.print(0); 94 | if(data < 10) 95 | Serial.print(0); 96 | Serial.print(data); 97 | Serial.print(" "); 98 | } 99 | Serial.println(""); 100 | } 101 | 102 | void loop() { 103 | // put your main code here, to run repeatedly: 104 | 105 | delay(100); 106 | 107 | Serial.println(""); 108 | 109 | trillBar.requestRawData(); 110 | if(trillBar.rawDataAvailable() > 0) { 111 | Serial.print("BAR "); 112 | printRawData(trillBar); 113 | } 114 | 115 | trillSquare.requestRawData(); 116 | if(trillSquare.rawDataAvailable() > 0) { 117 | Serial.print("SQUARE "); 118 | printRawData(trillSquare); 119 | } 120 | 121 | trillCraft.requestRawData(); 122 | if(trillCraft.rawDataAvailable() > 0) { 123 | Serial.print("CRAFT "); 124 | printRawData(trillCraft); 125 | } 126 | 127 | trillRing.requestRawData(); 128 | if(trillRing.rawDataAvailable() > 0) { 129 | Serial.print("RING "); 130 | printRawData(trillRing); 131 | } 132 | 133 | trillHex.requestRawData(); 134 | if(trillHex.rawDataAvailable() > 0) { 135 | Serial.print("HEX "); 136 | printRawData(trillHex); 137 | } 138 | 139 | trillFlex.requestRawData(); 140 | if(trillFlex.rawDataAvailable() > 0) { 141 | Serial.print("FLEX "); 142 | printRawData(trillFlex); 143 | } 144 | 145 | printLine(156, '_'); 146 | } 147 | -------------------------------------------------------------------------------- /calculateCentroids.h: -------------------------------------------------------------------------------- 1 | // returns a WORD packing two signed chars. The high bytes is the last active sensor in the last centroid, 2 | // while the low byte is the first active sensor of the last centroid 3 | WORD calculateCentroids(WORD *centroidBuffer, WORD *sizeBuffer, BYTE maxNumCentroids, BYTE minSensor, BYTE maxSensor, BYTE numSensors) { 4 | signed char lastActiveSensor = -1; 5 | BYTE centroidIndex = 0, sensorIndex, actualHardwareIndex; 6 | BYTE wrappedAround = 0; 7 | BYTE inCentroid = 0; 8 | WORD peakValue = 0, troughDepth = 0; 9 | BYTE counter; 10 | long temp; 11 | 12 | WORD lastSensorVal, currentSensorVal, currentWeightedSum, currentUnweightedSum; 13 | BYTE currentStart, currentLength; 14 | 15 | for(sensorIndex = 0; sensorIndex < maxNumCentroids; sensorIndex++) { 16 | centroidBuffer[sensorIndex] = 0xFFFF; 17 | sizeBuffer[sensorIndex] = 0; 18 | } 19 | 20 | currentSensorVal = 0; 21 | 22 | for(sensorIndex = 0, actualHardwareIndex = minSensor; sensorIndex < numSensors; sensorIndex++) 23 | { 24 | lastSensorVal = currentSensorVal; 25 | 26 | currentSensorVal = CSD_waSnsDiff[actualHardwareIndex++]; 27 | if(currentSensorVal > 0) { 28 | lastActiveSensor = sensorIndex; 29 | } 30 | // if we get to the end, and there is more to go, wrap around 31 | if(actualHardwareIndex == maxSensor) 32 | { 33 | actualHardwareIndex = minSensor; 34 | // once we wrap around, if we find ourselves out of a centroid, 35 | // any centroids detected after the then current point onwards 36 | // would be equal or worse than the ones we already got earlier for 37 | // the same sensors, so we will have to break 38 | wrappedAround = 1; 39 | } 40 | 41 | if(inCentroid) { 42 | // Currently in the middle of a group of sensors constituting a centroid. Use a zero sample 43 | // or a spike above a certain magnitude to indicate the end of the centroid. 44 | 45 | if(currentSensorVal == 0) { 46 | if(currentUnweightedSum > wMinimumCentroidSize) 47 | { 48 | temp = ((long)currentWeightedSum << SLIDER_BITS) / currentUnweightedSum; 49 | centroidBuffer[centroidIndex] = (currentStart << SLIDER_BITS) + (WORD)temp; 50 | sizeBuffer[centroidIndex] = currentUnweightedSum; 51 | centroidIndex++; 52 | } 53 | 54 | inCentroid = 0; 55 | if(wrappedAround) { 56 | break; 57 | } 58 | if(centroidIndex >= maxNumCentroids) 59 | break; 60 | continue; 61 | } 62 | 63 | if(currentSensorVal > peakValue) // Keep tabs on max and min values 64 | peakValue = currentSensorVal; 65 | if(peakValue - currentSensorVal > troughDepth) 66 | troughDepth = peakValue - currentSensorVal; 67 | 68 | // If this sensor value is a significant increase over the last one, AND the last one was decreasing, then start a new centroid. 69 | // In other words, identify a trough in the values and use it to segment into two centroids. 70 | 71 | if(sensorIndex >= 2) { 72 | if(troughDepth > wAdjacentCentroidNoiseThreshold && currentSensorVal > lastSensorVal + wAdjacentCentroidNoiseThreshold) { 73 | if(currentUnweightedSum > wMinimumCentroidSize) 74 | { 75 | temp = ((long)currentWeightedSum << SLIDER_BITS) / currentUnweightedSum; 76 | centroidBuffer[centroidIndex] = (currentStart << SLIDER_BITS) + (WORD)temp; 77 | sizeBuffer[centroidIndex] = currentUnweightedSum; 78 | centroidIndex++; 79 | } 80 | inCentroid = 0; 81 | if(wrappedAround){ 82 | break; 83 | } 84 | if(centroidIndex >= maxNumCentroids) 85 | break; 86 | inCentroid = 1; 87 | currentStart = sensorIndex; 88 | currentUnweightedSum = peakValue = currentSensorVal; 89 | currentLength = 1; 90 | currentWeightedSum = 0; 91 | troughDepth = 0; 92 | continue; 93 | } 94 | } 95 | 96 | currentUnweightedSum += currentSensorVal; 97 | currentWeightedSum += currentLength * currentSensorVal; 98 | currentLength++; 99 | } 100 | else { 101 | // Currently not in a centroid (zeros between centroids). Look for a new sample to initiate centroid. 102 | if(currentSensorVal > 0) { 103 | currentStart = sensorIndex; 104 | currentUnweightedSum = peakValue = currentSensorVal; 105 | currentLength = 1; 106 | currentWeightedSum = 0; 107 | troughDepth = 0; 108 | inCentroid = 1; 109 | } 110 | } 111 | if(!inCentroid && wrappedAround){ 112 | break; 113 | } 114 | } 115 | 116 | // Finish up the calculation on the last centroid, if necessary 117 | if(inCentroid && currentUnweightedSum > wMinimumCentroidSize) 118 | { 119 | temp = ((long)currentWeightedSum << SLIDER_BITS) / currentUnweightedSum; 120 | centroidBuffer[centroidIndex] = (currentStart << SLIDER_BITS) + (WORD)temp; 121 | sizeBuffer[centroidIndex] = currentUnweightedSum; 122 | centroidIndex++; 123 | } 124 | 125 | return (lastActiveSensor << 8) | currentStart; 126 | } 127 | -------------------------------------------------------------------------------- /examples/craft-settings/craft-settings.ino: -------------------------------------------------------------------------------- 1 | /* 2 | ____ _____ _ _ 3 | | __ )| ____| | / \ 4 | | _ \| _| | | / _ \ 5 | | |_) | |___| |___ / ___ \ 6 | |____/|_____|_____/_/ \_\ 7 | http://bela.io 8 | 9 | \example craft-settings 10 | 11 | Adjusting Trill Settings 12 | ======================== 13 | 14 | This example will work with all types of Trill sensor and will allow you to adjust 15 | the sensitivity and threshold settings but it is set for Trill Craft by default as 16 | it is where these changes would be more obvious and necessary.. 17 | 18 | The first thing to do is make sure that the correct sensor type is 19 | given to `touchSensor.setup();`. If you have changed the address of the sensor 20 | then you will need to add that to this function too. 21 | 22 | There are two important sensor settings that you may want to adjust when working 23 | with the Trill sensors: the `threshold` and the `prescaler`. 24 | 25 | The `threshold` setting is simply the threshold above which to read and is for 26 | ignoring any noise that might be present in the lowest regions of the sensor reading. 27 | 28 | The `prescaler` setting equates to the sensitivity of the sensor. Technically, this 29 | value is a divider for the clock on the Cypress chip and so it decides how long the 30 | chip charges the connected material for before taking a reading. There are 8 different 31 | settings for the prescaler. 32 | 33 | The rule of thumb when adjusting these values is: 34 | - A higher value prescaler (i.e. longer charging time as it is a divider of the clock) 35 | is better for more resistive materials and larger conductive objects connected. 36 | - A lower value prescaler is better for proximity sensing. 37 | 38 | When connecting different materials to Trill Craft we recommend experimenting with 39 | the settings using this example. 40 | 41 | Parameters can be adjusted via the Arduino Serial Monitor by writting one-line commands 42 | followed by a semicolon and the value to be set: 43 | ``` 44 | prescaler: prescalerValue 45 | threshold: thresholdValue 46 | bits: numberOfBits 47 | ``` 48 | 49 | Commands can also be used to update the sensor baseline: 50 | ``` 51 | baseline 52 | ``` 53 | 54 | or to change sensor mode of operation: 55 | ``` 56 | mode: sensorMode 57 | ``` 58 | (where sensorMode can be one of {centroid, raw, baseline, differential}). 59 | 60 | This example also prints the raw data from the sensor, which is useful to see the effects 61 | of changing the different parameters (specially for Trill Craft).. This can be toggled 62 | on and off by sending the character 't' over the Serial Monitor. 63 | */ 64 | 65 | 66 | #include 67 | 68 | Trill trillSensor; 69 | 70 | String serialInput; 71 | char gCommandToken = ':'; 72 | char gEndToken = '\n'; 73 | 74 | long gLastMillis = 0; 75 | 76 | bool printSensorVal = true; 77 | 78 | void setup() { 79 | // Initialise serial and touch sensor 80 | Serial.begin(115200); 81 | int ret; 82 | while((ret = trillSensor.setup(Trill::TRILL_CRAFT))) { 83 | Serial.println("failed to initialise trillSensor"); 84 | Serial.println("Error code: "); 85 | Serial.println(ret); 86 | Serial.println("\n"); 87 | } 88 | } 89 | 90 | void loop() { 91 | 92 | if(Serial.available() > 0) { 93 | serialInput = Serial.readStringUntil(gEndToken); 94 | serialInput.toLowerCase(); 95 | serialInput.trim(); 96 | if(serialInput == "t") { 97 | printSensorVal = !printSensorVal; 98 | } else { 99 | int delimiterIndex = serialInput.indexOf(gCommandToken); 100 | String command = serialInput.substring(0, delimiterIndex); 101 | command.trim(); 102 | String commandValue = serialInput.substring(delimiterIndex+1); 103 | commandValue.trim(); 104 | 105 | if(command == "prescaler") { 106 | Serial.print("setting prescaler to "); 107 | Serial.println(commandValue.toInt()); 108 | trillSensor.setPrescaler(commandValue.toInt()); 109 | gLastMillis = millis(); // Give 100ms for the chip to catch up 110 | } else if(command == "baseline") { 111 | Serial.println("updating baseline"); 112 | trillSensor.updateBaseline(); 113 | gLastMillis = millis(); // Give 100ms for the chip to catch up 114 | } else if(command == "threshold") { 115 | Serial.print("setting noise threshold to "); 116 | Serial.println(commandValue.toInt()); 117 | trillSensor.setNoiseThreshold(commandValue.toInt()); 118 | gLastMillis = millis(); // Give 100ms for the chip to catch up 119 | } else if(command == "bits") { 120 | Serial.print("setting numBits to "); 121 | Serial.println(commandValue.toInt()); 122 | trillSensor.setScanSettings(0, commandValue.toInt()); 123 | gLastMillis = millis(); // Give 100ms for the chip to catch up 124 | } else if(command == "mode") { 125 | Serial.print("setting mode to "); 126 | Serial.println(commandValue); 127 | trillSensor.setMode(modeFromString(commandValue)); 128 | gLastMillis = millis(); // Give 100ms for the chip to catch up 129 | } else { 130 | Serial.println("unknown command"); 131 | } 132 | } 133 | } 134 | 135 | if(printSensorVal) { 136 | if(millis() - gLastMillis > 100) { 137 | gLastMillis += 100; 138 | trillSensor.requestRawData(); 139 | 140 | if(trillSensor.rawDataAvailable() > 0) { 141 | while(trillSensor.rawDataAvailable() > 0) { 142 | int data = trillSensor.rawDataRead(); 143 | if(data < 1000) 144 | Serial.print(0); 145 | if(data < 100) 146 | Serial.print(0); 147 | if(data < 10) 148 | Serial.print(0); 149 | Serial.print(data); 150 | Serial.print(" "); 151 | } 152 | Serial.println(""); 153 | } 154 | } 155 | } 156 | } 157 | 158 | Trill::Mode modeFromString(String & modeString) { 159 | modeString.toLowerCase(); 160 | if(modeString == "centroid") { 161 | return Trill::CENTROID; 162 | } else if(modeString == "raw") { 163 | return Trill::RAW; 164 | } else if(modeString == "baseline" || modeString == "base") { 165 | return Trill::BASELINE; 166 | } else if(modeString == "differential" || modeString == "diff") { 167 | return Trill::DIFF; 168 | } 169 | return Trill::AUTO; 170 | } 171 | -------------------------------------------------------------------------------- /Trill.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Trill library for Arduino 3 | * (c) 2020 bela.io 4 | * 5 | * This library communicates with the Trill sensors 6 | * using I2C. 7 | * 8 | * BSD license 9 | */ 10 | 11 | #ifndef TRILL_H 12 | #define TRILL_H 13 | 14 | #if (ARDUINO >= 100) 15 | #include "Arduino.h" 16 | #else 17 | #include "WProgram.h" 18 | #endif 19 | #include "Wire.h" 20 | 21 | #define TRILL_SPEED_ULTRA_FAST 0 22 | #define TRILL_SPEED_FAST 1 23 | #define TRILL_SPEED_NORMAL 2 24 | #define TRILL_SPEED_SLOW 3 25 | 26 | class Touches 27 | { 28 | public: 29 | Touches() { num_touches = 0; }; 30 | typedef uint16_t TouchData_t; 31 | /* How many touches? */ 32 | uint8_t getNumTouches() const; 33 | /* Location and size of a particular touch, ranging from 0 to N-1. 34 | Returns -1 if no such touch exists. */ 35 | int touchLocation(uint8_t touch_num) const; 36 | int touchSize(uint8_t touch_num) const; 37 | void processCentroids(uint8_t maxCentroids); 38 | TouchData_t const* centroids; 39 | TouchData_t const* sizes; 40 | uint8_t num_touches;/* Number of touches. Updated by processCentroids() */ 41 | }; 42 | 43 | class Touches2D : public Touches 44 | { 45 | public: 46 | unsigned int getNumHorizontalTouches(); 47 | int touchHorizontalLocation(uint8_t touch_num); 48 | int touchHorizontalSize(uint8_t touch_num); 49 | protected: 50 | Touches2D() {}; 51 | Touches horizontal; 52 | }; 53 | 54 | class Trill : public Touches2D 55 | { 56 | public: 57 | Trill(); 58 | 59 | enum Mode { 60 | AUTO = -1, 61 | CENTROID = 0, 62 | RAW = 1, 63 | BASELINE = 2, 64 | DIFF = 3, 65 | NUM_MODES = 5 66 | }; 67 | 68 | enum Device { 69 | TRILL_NONE = -1, 70 | TRILL_UNKNOWN = 0, 71 | TRILL_BAR = 1, 72 | TRILL_SQUARE = 2, 73 | TRILL_CRAFT = 3, 74 | TRILL_RING = 4, 75 | TRILL_HEX = 5, 76 | TRILL_FLEX = 6, 77 | TRILL_NUM_DEVICES = 7 78 | }; 79 | 80 | struct TrillDefaults 81 | { 82 | Trill::Device device; 83 | Trill::Mode mode; 84 | uint8_t address; 85 | }; 86 | 87 | struct TrillDefaults trillDefaults[TRILL_NUM_DEVICES + 1] = { 88 | {TRILL_NONE, AUTO, 0xFF}, 89 | {TRILL_UNKNOWN, AUTO, 0xFF}, 90 | {TRILL_BAR, CENTROID, 0x20}, 91 | {TRILL_SQUARE, CENTROID, 0x28}, 92 | {TRILL_CRAFT, DIFF, 0x30}, 93 | {TRILL_RING, CENTROID, 0x38}, 94 | {TRILL_HEX, CENTROID, 0x40}, 95 | {TRILL_FLEX, DIFF, 0x48}, 96 | }; 97 | 98 | static constexpr uint8_t interCommandDelay = 15; 99 | /** 100 | * An array containing the valid values for the speed parameter 101 | * in setScanSettings() 102 | */ 103 | static constexpr uint8_t speedValues[4] = {0, 1, 2, 3}; 104 | /** 105 | * The maximum value for the setPrescaler() method 106 | */ 107 | static constexpr uint8_t prescalerMax = 8; 108 | 109 | 110 | /* Initialise the hardware */ 111 | int begin(Device device, uint8_t i2c_address = 255, TwoWire* wire = &Wire); 112 | /* Initialise the hardware, it's the same as begin() */ 113 | int setup(Device device, uint8_t i2c_address = 255, TwoWire* wire = &Wire) { return begin(device, i2c_address, wire); } 114 | 115 | /* --- Main communication --- */ 116 | 117 | /* Return the type of device attached, or 0 if none is attached. 118 | Same as begin(), but without re-initialising the system. */ 119 | int identify(); 120 | 121 | /** 122 | * Does the device have one axis of position sensing? 123 | * 124 | * @return `true` if the device has one axis of position sensing 125 | * and is set in #CENTROID mode, `false` 126 | * otherwise. 127 | */ 128 | bool is1D(); 129 | /** 130 | * Does the device have two axes of position sensing? 131 | * 132 | * @return `true` if the device has two axes of position sensing 133 | * and is set in #CENTROID mode, `false` 134 | * otherwise. 135 | */ 136 | bool is2D(); 137 | 138 | static Device probe(uint8_t i2c_address) { 139 | Trill t; 140 | 141 | /* Start I2C */ 142 | t.begin(Trill::TRILL_UNKNOWN, i2c_address); 143 | 144 | /* Check the type of device attached */ 145 | if(t.identify() != 0) { 146 | // Unable to identify device 147 | return Trill::TRILL_NONE; 148 | } 149 | return t.deviceType(); 150 | } 151 | 152 | /* Return the device type already identified */ 153 | Device deviceType() { return device_type_; }; 154 | 155 | /* Get the name of a given device */ 156 | static const char* getNameFromDevice(Device device); 157 | 158 | /* Return firmware version */ 159 | int firmwareVersion() { return firmware_version_; } 160 | 161 | /* Get the mode that the device is currently in */ 162 | Mode getMode() { return mode_; } 163 | 164 | /* Get the current address of the device */ 165 | uint8_t getAddress() { return i2c_address_; } 166 | 167 | /* Get the number of capacitive channels on the device */ 168 | unsigned int getNumChannels(); 169 | 170 | /* Return the number of "button" channels on the device */ 171 | unsigned int getNumButtons() { return 2 * (getMode() == CENTROID && TRILL_RING == deviceType());}; 172 | 173 | /* Read the latest scan value from the sensor. Returns true on success. */ 174 | boolean read(); 175 | 176 | /* Update the baseline value on the sensor */ 177 | void updateBaseline(); 178 | 179 | /* --- Data processing --- */ 180 | 181 | /* Button value for Ring? */ 182 | int getButtonValue(uint8_t button_num); 183 | 184 | /* --- Raw data handling --- */ 185 | 186 | /* Request raw data; wrappers for Wire */ 187 | boolean requestRawData(uint8_t max_length = 0xFF); 188 | int rawDataAvailable(); 189 | int rawDataRead(); 190 | 191 | /* --- Scan configuration settings --- */ 192 | void setMode(Mode mode); 193 | void setScanSettings(uint8_t speed, uint8_t num_bits); 194 | void setPrescaler(uint8_t prescaler); 195 | void setNoiseThreshold(uint8_t threshold); 196 | void setIDACValue(uint8_t value); 197 | void setMinimumTouchSize(uint16_t size); 198 | void setAutoScanInterval(uint16_t interval); 199 | 200 | private: 201 | void prepareForDataRead(); 202 | 203 | enum { 204 | kCommandNone = 0, 205 | kCommandMode = 1, 206 | kCommandScanSettings = 2, 207 | kCommandPrescaler = 3, 208 | kCommandNoiseThreshold = 4, 209 | kCommandIdac = 5, 210 | kCommandBaselineUpdate = 6, 211 | kCommandMinimumSize = 7, 212 | kCommandAutoScanInterval = 16, 213 | kCommandIdentify = 255 214 | }; 215 | 216 | enum { 217 | kOffsetCommand = 0, 218 | kOffsetData = 4 219 | }; 220 | 221 | enum { 222 | kMaxTouchNum1D = 5, 223 | kMaxTouchNum2D = 4 224 | }; 225 | 226 | enum { 227 | kCentroidLengthDefault = 20, 228 | kCentroidLengthRing = 24, 229 | kCentroidLength2D = 32, 230 | kRawLength = 60 231 | }; 232 | 233 | enum { 234 | kNumChannelsBar = 26, 235 | kNumChannelsRing = 30, 236 | kNumChannelsMax = 30 237 | }; 238 | 239 | enum { 240 | kRawLengthBar = 52, 241 | kRawLengthHex = 60, 242 | kRawLengthRing = 56 243 | }; 244 | TwoWire* wire_; 245 | 246 | Device device_type_; /* Which type of device is connected, if any */ 247 | Mode mode_; /* Which mode the device is in */ 248 | uint8_t firmware_version_; /* Firmware version running on the device */ 249 | uint8_t last_read_loc_; /* Which byte reads will begin from on the device */ 250 | uint8_t raw_bytes_left_; /* How many bytes still remaining to request? */ 251 | uint8_t i2c_address_; /* Address of this slider on I2C bus */ 252 | 253 | uint16_t buffer_[kCentroidLength2D * 2];/* Buffer for centroid response */ 254 | }; 255 | 256 | // first template argument is the max num of centroids 257 | // the second argument is the number of readings that will be processed at the 258 | // same time. This should be 0 if the data passed to process() is already ordered 259 | template 260 | class CentroidDetection : public Touches 261 | { 262 | public: 263 | typedef uint16_t WORD; 264 | CentroidDetection() {}; 265 | CentroidDetection(const unsigned int* order); 266 | int begin(const uint8_t* order, unsigned int numReadings) { 267 | return setup(order, numReadings); 268 | } 269 | // pass nullptr if the data passed to process() is already ordered. 270 | int setup(const uint8_t* order, unsigned int orderLength) { 271 | this->order = order; 272 | Touches::centroids = this->centroids; 273 | Touches::sizes = this->sizes; 274 | num_touches = 0; 275 | this->orderLength = orderLength; 276 | if(orderLength > _numReadings) 277 | return -1; // cannot work with more than _numReadings 278 | return 0; 279 | } 280 | 281 | void process(const WORD* rawData) { 282 | uint8_t nMax = _numReadings < orderLength ? _numReadings : orderLength; 283 | if(order) { 284 | for(unsigned int n = 0; n < nMax; ++n) { 285 | data[n] = rawData[order[n]]; 286 | } 287 | cc.CSD_waSnsDiff = data; 288 | } else { 289 | // no reordering needed 290 | cc.CSD_waSnsDiff = rawData; 291 | } 292 | cc.calculateCentroids(centroids, sizes, _maxNumCentroids, 0, nMax, nMax); 293 | processCentroids(_maxNumCentroids); 294 | } 295 | 296 | void setMinimumTouchSize(TouchData_t minSize) { 297 | cc.wMinimumCentroidSize = minSize; 298 | } 299 | 300 | private: 301 | // a small helper class, whose main purpose is to wrap the #include 302 | // and make all the variables related to it private and multi-instance safe 303 | class CalculateCentroids 304 | { 305 | public: 306 | typedef uint8_t BYTE; 307 | WORD const * CSD_waSnsDiff; 308 | WORD wMinimumCentroidSize = 0; 309 | BYTE SLIDER_BITS = 7; 310 | WORD wAdjacentCentroidNoiseThreshold = 400; // Trough between peaks needed to identify two centroids 311 | //WORD calculateCentroids(WORD *centroidBuffer, WORD *sizeBuffer, BYTE maxNumCentroids, BYTE minSensor, BYTE maxSensor, BYTE numSensors); 312 | // calculateCentroids is defined here: 313 | #include "calculateCentroids.h" 314 | }; 315 | TouchData_t centroids[_maxNumCentroids]; 316 | TouchData_t sizes[_maxNumCentroids * 2]; 317 | const uint8_t* order; 318 | unsigned int orderLength; 319 | WORD data[_numReadings]; 320 | CalculateCentroids cc; 321 | }; 322 | 323 | class CustomSlider : public CentroidDetection<5, 30> {}; 324 | #endif /* TRILL_H */ 325 | -------------------------------------------------------------------------------- /examples/trill-connect/trill-connect.ino: -------------------------------------------------------------------------------- 1 | /* 2 | trill-connect: a Swiss-army knife sketch for Trill connectivity. 3 | 4 | Connect your Trill to your computer, handheld or analog equipment via an ESP32-S3 board. 5 | This was deesigned for the ESP32-S3 and tested on ESP32-S3-DEVKIT-C1 v1.0, selecting the "Adafruit 6 | Feather ESP32-S3 No PSRAM" board (adafruit_feather_esp32s3_nopsram) in the Arduino-IDE. It should 7 | work with minimal (e.g.: pin numbers only) modifications on most ESP32-S3 boards and it may work 8 | to some extent on other boards with USB and/or BLE support. 9 | 10 | Connect a Square and/or a Bar using external pull-ups and/or a Trill Hub, using pins 47 for SDA 11 | and 21 for SCL. 12 | Available features: 13 | - USB_MOUSE/BLE_MOUSE: the board shows up on the host as a USB and/or BLE mouse, respectively. 14 | The Square acts as a trackpad, while the Bar acts as a scroll wheel. There is no support for 15 | clicks (it's complicated without a pressure sensor or a dedicated button!). 16 | - USB_MIDI/BLE_MIDI: the board shows up on the host as a USB and/or BLE MIDI device, 17 | respectively. High-resolution (14-bit, MSB + LSB) control change messages are send via USB, 18 | while for BLE we use 7-bit (MSB only) CCs in order to reduce the bandwidth. The enclosed PureData 19 | patch receives, interprets and labels the high-precision CC values. This sends: 20 | - square x position on CC 5 (MSB) and CC 37 (LSB) 21 | - square y position on CC 6 (MSB) and CC 38 (LSB) 22 | - square touch size on CC 7 (MSB) and CC 39 (LSB) 23 | - bar position on CC 8 (MSB) and CC 40 (LSB) 24 | - bar touch size on CC 9 (MSB) and CC 41 (LSB) 25 | - ANALOG_OUT: sends touch information to the "analog" (actually PWM) outputs using the parameters 26 | set in `pwmBaseFreq` and `pwmResolutionBits`, starting from the GPIO channel set in `pwmFirstGpio`. 27 | With the default settings (39kHz carrier, 10 bit), a passive low-pass RC filter using a 10k 28 | resistor and 22n capacitor already gives good results. The order of the outputs (starting from 29 | `pwmFirstGpio`) is: 30 | - square x position (default: 35) 31 | - square y position (default: 36) 32 | - square touch size (default: 37) 33 | - bar position (default: 38) 34 | - bar touch size (default: 39) 35 | 36 | Enable individual features below. Simultaneous use of most of them is allowed, with the only exception being 37 | that BLE_MOUSE and BLE_MIDI which are mutually exclusive. 38 | Note that enabling and using one interface may slow down the performance of other interfaces, as they are all 39 | accessed from the same loop. 40 | */ 41 | 42 | #define USB_MOUSE // uses "Adafruit TinyUSB Library" 43 | #define USB_MIDI // uses "Adafruit TinyUSB Library" and "MIDI Library" 44 | #define BLE_MOUSE // requires manual install of github.com/T-vK/ESP32-BLE-Mouse 45 | // #define BLE_MIDI // uses "ESP32-BLE-MIDI" and "MIDI Library" 46 | #define ANALOG_OUT 47 | 48 | #if defined(BLE_MOUSE) && defined(BLE_MIDI) 49 | #error BLE_MOUSE and BLE_MIDI are not allowed together 50 | // this is because ESP32-BLE-Mouse uses the core BLE (BLEDevice.h, BLEUtils.h, etc) while 51 | #endif 52 | 53 | #ifdef USB_MOUSE 54 | #include "Adafruit_TinyUSB.h" 55 | // HID report descriptor using TinyUSB's template 56 | // Single Report (no ID) descriptor 57 | uint8_t const desc_hid_report[] = { 58 | TUD_HID_REPORT_DESC_MOUSE() 59 | }; 60 | // USB HID object. For ESP32 these values cannot be changed after this declaration 61 | // desc report, desc len, protocol, interval, use out endpoint 62 | Adafruit_USBD_HID mouse(desc_hid_report, sizeof(desc_hid_report), HID_ITF_PROTOCOL_MOUSE, 2, false); 63 | #endif // USB_MOUSE 64 | 65 | #ifdef BLE_MOUSE 66 | #include 67 | BleMouse bleMouse("Trill BLE Mouse", "Augmented Instruments Ltd"); 68 | #endif // BLE_MOUSE 69 | #ifdef BLE_MIDI 70 | #include 71 | #endif // BLE_MIDI 72 | 73 | void mouseBegin() { 74 | #ifdef USB_MOUSE 75 | mouse.begin(); 76 | #endif // USB_MOUSE 77 | #ifdef BLE_MOUSE 78 | bleMouse.begin(); 79 | #endif // BLE_MOUSE 80 | } 81 | 82 | void mouseMove(int8_t x, int8_t y, int8_t scroll) { 83 | #ifdef USB_MOUSE 84 | if (TinyUSBDevice.mounted()) 85 | mouse.mouseReport(0, 0, x, y, scroll, 0); 86 | #endif // USB_MOUSE 87 | #ifdef BLE_MOUSE 88 | if (bleMouse.isConnected()) 89 | bleMouse.move(x, y, scroll); 90 | #endif // BLE_MOUSE 91 | } 92 | 93 | #ifdef USB_MIDI 94 | #include 95 | #include 96 | Adafruit_USBD_MIDI usb_midi; 97 | MIDI_CREATE_INSTANCE(Adafruit_USBD_MIDI, usb_midi, MIDI); 98 | #endif // USB_MIDI 99 | 100 | void midiBegin() { 101 | #ifdef BLE_MIDI 102 | BLEMidiServer.begin("Trill MIDI"); 103 | // BLEMidiServer.enableDebugging(); // Uncomment if you want to see some debugging output from the library (not much for the server class...) 104 | #endif // BLE_MIDI 105 | } 106 | 107 | void controlChangeHighRes(byte channel, byte control, unsigned value) { 108 | uint8_t msb = (value >> 7) & 0x7f; 109 | #ifdef USB_MIDI 110 | if (TinyUSBDevice.mounted()) { 111 | // send a high-resolution (14-bit) CC message: 112 | // (note that the MIDI class API numbers channels from 1:16 instead of 0:15) 113 | uint8_t lsb = value & 0x7f; 114 | MIDI.sendControlChange(control, lsb, channel + 1 + 32); // send LSB at CC + 32 115 | MIDI.sendControlChange(control, msb, channel + 1); // send MSB 116 | } 117 | #endif // USB_MIDI 118 | #ifdef BLE_MIDI 119 | // send regular-resolution (7-bit) CC to save bandwidth 120 | if (BLEMidiServer.isConnected()) { 121 | BLEMidiServer.controlChange(channel, control, msb); 122 | } 123 | #endif // BLE_MIDI 124 | } 125 | 126 | #ifdef ANALOG_OUT 127 | uint32_t pwmBaseFreq = 39000; 128 | constexpr uint32_t pwmResolutionBits = 10; 129 | unsigned int pwmFirstGpio = 35; 130 | 131 | // Arduino-like analogWrite (i.e.: PWM) 132 | // value has to be between 0 and ((1 << pwmResolutionBits) - 1) 133 | void analogOut(uint8_t channel, uint32_t value) { 134 | #ifdef ANALOG_OUT 135 | uint32_t duty = min(value, uint32_t((1 << pwmResolutionBits) - 1)); 136 | ledcWrite(channel, duty); 137 | #endif // ANALOG_OUT 138 | } 139 | #endif 140 | 141 | #include 142 | Trill bar; 143 | Trill square; 144 | 145 | constexpr unsigned RGB_LED_GPIO = 48; // On ESP32-S3-DevKit-C1 v1.0 146 | void setup() { 147 | Serial.begin(115200); 148 | pinMode(RGB_LED_GPIO, OUTPUT); 149 | neopixelWrite(RGB_LED_GPIO, 0, 0, 255); 150 | // give the USB host enough time to connect to the sw serial port so no log is lost 151 | delay(5000); 152 | neopixelWrite(RGB_LED_GPIO, 0, 255, 0); 153 | Serial.println("SETUP"); 154 | int ret; 155 | // set pins foir I2C: 47 is SDA and 21 is SCL 156 | ret = Wire.setPins(47, 21); 157 | ret = bar.setup(Trill::TRILL_BAR); 158 | Serial.printf("bar.setup() returned %d\n\r", ret); 159 | ret = square.setup(Trill::TRILL_SQUARE); 160 | Serial.printf("square.setup() returned %d\n\r", ret); 161 | mouseBegin(); 162 | midiBegin(); 163 | #ifdef ANALOG_OUT 164 | for (unsigned n = 0; n < 5; ++n) { 165 | // set up the PWM channels 166 | int actualFreq = ledcSetup(n, pwmBaseFreq, pwmResolutionBits); 167 | Serial.printf("Requested freq %d on channel %d, obtained %d\n\r", pwmBaseFreq, n, actualFreq); 168 | ledcAttachPin(pwmFirstGpio + n, n); 169 | } 170 | #endif // ANALOG_OUT 171 | neopixelWrite(RGB_LED_GPIO, 0, 0, 0); 172 | } 173 | 174 | int pastMillis = 0; 175 | void loop() { 176 | // do not scan Trills more often than once every 7 ms 177 | while (millis() - pastMillis < 7) 178 | yield(); 179 | pastMillis = millis(); 180 | bar.read(); 181 | square.read(); 182 | 183 | int scroll = 0; 184 | int x = 0; 185 | int y = 0; 186 | static unsigned long pastBarMs = millis(); 187 | static int barHadTouch = 0; 188 | int barHasTouch = bar.getNumTouches(); 189 | if (millis() - pastBarMs > 100) { // send scroll every 100 ms 190 | if (barHasTouch && bar.touchSize(0) > 500) { 191 | static int pastBar = bar.touchLocation(0); 192 | int val = bar.touchLocation(0); 193 | if (barHadTouch) { 194 | pastBarMs = millis(); 195 | int diff = val - pastBar; 196 | if (abs(diff) > 20) 197 | scroll = diff > 0 ? 1 : -1; 198 | } 199 | pastBar = val; 200 | } 201 | } 202 | if (barHasTouch || barHadTouch) { 203 | unsigned int pos = 0; 204 | unsigned int size = 0; 205 | if (barHasTouch) { 206 | pos = bar.touchLocation(0); 207 | // remap to cover the full 14-bit range 208 | pos = map(pos, 0, 128 * 25, 0, 16383); 209 | pos = constrain(pos, 0, 16383); 210 | // enlarge to size to cover enough of the 14-bit range 211 | size = bar.touchSize(0) * 4; 212 | size = constrain(size, 0, 16383); 213 | } 214 | controlChangeHighRes(0, 8, pos); 215 | controlChangeHighRes(0, 9, size); 216 | #ifdef ANALOG_OUT 217 | analogOut(3, pos >> (14 - pwmResolutionBits)); 218 | analogOut(4, size >> (14 - pwmResolutionBits)); 219 | #endif // ANALOG_OUT 220 | } 221 | barHadTouch = barHasTouch; 222 | 223 | static int squareHadTouch = 0; 224 | int squareHasTouch = square.getNumTouches(); 225 | int thisX = 0; 226 | int thisY = 0; 227 | if (squareHasTouch && square.touchSize(0) > 500) { 228 | static int pastSquareX; 229 | static int pastSquareY; 230 | thisX = square.touchHorizontalLocation(0); 231 | thisY = square.touchLocation(0); 232 | if (squareHadTouch) { 233 | // only consider movement if we have a valid past value 234 | x = (thisX - pastSquareX) / 2; 235 | y = -(thisY - pastSquareY) / 2; // - to fix the orientation so that it matches the screen 236 | } 237 | pastSquareX = thisX; 238 | pastSquareY = thisY; 239 | } 240 | if (squareHasTouch || squareHadTouch) { 241 | unsigned int xcc = 0; 242 | unsigned int ycc = 0; 243 | unsigned int size = 0; 244 | if (squareHasTouch) { 245 | // remap coordinates to cover the full 14-bit range 246 | xcc = map(thisX, 256, 128 * 14, 0, 16383); 247 | xcc = constrain(xcc, 0, 16383); 248 | ycc = map(thisY, 256, 128 * 14, 0, 16383); 249 | ycc = constrain(ycc, 0, 16383); 250 | // enlarge to size to cover a reasonable portion of the 14-bit range 251 | size = square.touchSize(0) * 4; 252 | } 253 | controlChangeHighRes(0, 5, xcc); 254 | controlChangeHighRes(0, 6, ycc); 255 | controlChangeHighRes(0, 7, size); 256 | #ifdef ANALOG_OUT 257 | analogOut(0, xcc >> (14 - pwmResolutionBits)); 258 | analogOut(1, ycc >> (14 - pwmResolutionBits)); 259 | analogOut(2, size >> (14 - pwmResolutionBits)); 260 | #endif // ANALOG_OUT 261 | } 262 | squareHadTouch = squareHasTouch; 263 | 264 | if (x || y || scroll) { 265 | mouseMove(x, y, scroll); 266 | } 267 | neopixelWrite(RGB_LED_GPIO, !(x || y || scroll) * 10, (x || y) * 120, !!scroll * 120); 268 | } 269 | -------------------------------------------------------------------------------- /Trill.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Trill library for Arduino 3 | * (c) 2020 bela.io 4 | * 5 | * This library communicates with the Trill sensors 6 | * using I2C. 7 | * 8 | * BSD license 9 | */ 10 | 11 | #include "Trill.h" 12 | 13 | // some implementations of Wire (e.g.: Particle OS) do not define BUFFER_LENGTH 14 | #ifndef BUFFER_LENGTH 15 | #define BUFFER_LENGTH 32 16 | #endif // BUFFER_LENGTH 17 | 18 | #define MAX_TOUCH_1D_OR_2D (((device_type_ == TRILL_SQUARE || device_type_ == TRILL_HEX) ? kMaxTouchNum2D : kMaxTouchNum1D)) 19 | #define RAW_LENGTH ((device_type_ == TRILL_BAR ? 2 * kNumChannelsBar \ 20 | : device_type_ == TRILL_RING ? 2 * kNumChannelsRing \ 21 | : 2 * kNumChannelsMax)) 22 | 23 | Trill::Trill() 24 | : wire_(&Wire), device_type_(TRILL_NONE), mode_(AUTO), 25 | firmware_version_(0), last_read_loc_(0xFF), raw_bytes_left_(0) 26 | { 27 | } 28 | 29 | /* Initialise the hardware. Returns the type of device attached, or 0 30 | if none is attached. */ 31 | int Trill::begin(Device device, uint8_t i2c_address, TwoWire* wire) { 32 | wire_ = wire; 33 | 34 | if(128 <= i2c_address) 35 | i2c_address = trillDefaults[device+1].address; 36 | 37 | /* Unknown default address */ 38 | if(128 <= i2c_address) { 39 | return -2; 40 | } 41 | 42 | i2c_address_ = i2c_address; 43 | 44 | /* Start I2C */ 45 | wire_->begin(); 46 | 47 | /* Check the type of device attached */ 48 | if(identify() != 0) { 49 | // Unable to identify device 50 | return 2; 51 | } 52 | 53 | /* Check for wrong device type */ 54 | if(TRILL_UNKNOWN != device && device_type_ != device) { 55 | device_type_ = TRILL_NONE; 56 | return -3; 57 | } 58 | 59 | /* Check for device mode */ 60 | Mode mode = trillDefaults[device+1].mode; 61 | if(AUTO == mode) { 62 | return -1; 63 | } 64 | 65 | /* Put the device in the correspondent mode */ 66 | setMode(mode); 67 | delay(interCommandDelay); 68 | 69 | Touches::centroids = buffer_; 70 | Touches::sizes = buffer_ + MAX_TOUCH_1D_OR_2D; 71 | if(is2D()) { 72 | horizontal.centroids = buffer_ + 2 * MAX_TOUCH_1D_OR_2D; 73 | horizontal.sizes = buffer_ + 3 * MAX_TOUCH_1D_OR_2D; 74 | } else 75 | horizontal.num_touches = 0; 76 | 77 | /* Set default scan settings */ 78 | setScanSettings(0, 12); 79 | delay(interCommandDelay); 80 | 81 | updateBaseline(); 82 | delay((firmware_version_ >= 3 ? 10 : 1) * interCommandDelay); // not really needed, but it ensures the first command the user sends after calling setup() will be adequately timed. Hopefully this is not a source of confusion... 83 | 84 | return 0; 85 | } 86 | 87 | /* Return the type of device attached, or 0 if none is attached. */ 88 | int Trill::identify() { 89 | wire_->beginTransmission(i2c_address_); 90 | wire_->write(kOffsetCommand); 91 | wire_->write(kCommandIdentify); 92 | int ret = wire_->endTransmission(); 93 | if(ret) 94 | return ret; 95 | 96 | /* Give Trill time to process this command */ 97 | delay(25); 98 | 99 | last_read_loc_ = kOffsetCommand; 100 | wire_->requestFrom(i2c_address_, (uint8_t)3); 101 | 102 | if(wire_->available() < 3) { 103 | /* Unexpected or no response; no valid device connected */ 104 | device_type_ = TRILL_NONE; 105 | firmware_version_ = 0; 106 | return -1; 107 | } 108 | 109 | wire_->read(); // Discard first input 110 | device_type_ = (Device)wire_->read(); 111 | firmware_version_ = wire_->read(); 112 | 113 | return 0; 114 | } 115 | 116 | /* Get the name of a given device */ 117 | const char* Trill::getNameFromDevice(Device device) { 118 | switch(device) { 119 | case TRILL_BAR: 120 | return "Bar"; 121 | case TRILL_SQUARE: 122 | return "Square"; 123 | case TRILL_RING: 124 | return "Ring"; 125 | case TRILL_FLEX: 126 | return "Flex"; 127 | case TRILL_HEX: 128 | return "Hex"; 129 | default: 130 | return "NO DEVICE"; 131 | } 132 | } 133 | 134 | /* Read the latest scan value from the sensor. Returns true on success. */ 135 | boolean Trill::read() { 136 | if(CENTROID != mode_) 137 | return false; 138 | uint8_t loc = 0; 139 | uint8_t length = kCentroidLengthDefault; 140 | 141 | /* Set the read location to the right place if needed */ 142 | prepareForDataRead(); 143 | 144 | if(device_type_ == TRILL_SQUARE || device_type_ == TRILL_HEX) 145 | length = kCentroidLength2D; 146 | 147 | if(device_type_ == TRILL_RING) 148 | length = kCentroidLengthRing; 149 | 150 | wire_->requestFrom(i2c_address_, length); 151 | while(wire_->available() >= 2) { 152 | uint8_t msb = wire_->read(); 153 | uint8_t lsb = wire_->read(); 154 | buffer_[loc] = lsb + (msb << 8); 155 | ++loc; 156 | } 157 | 158 | uint8_t maxNumCentroids = MAX_TOUCH_1D_OR_2D; 159 | boolean ret = true; 160 | /* Check for read error */ 161 | if(loc * 2 < length) { 162 | maxNumCentroids = 0; 163 | ret = false; 164 | } 165 | 166 | processCentroids(maxNumCentroids); 167 | if(is2D()) 168 | horizontal.processCentroids(maxNumCentroids); 169 | 170 | return ret; 171 | } 172 | 173 | /* Update the baseline value on the sensor */ 174 | void Trill::updateBaseline() { 175 | wire_->beginTransmission(i2c_address_); 176 | wire_->write(kOffsetCommand); 177 | wire_->write(kCommandBaselineUpdate); 178 | wire_->endTransmission(); 179 | 180 | last_read_loc_ = kOffsetCommand; 181 | } 182 | 183 | /* Request raw data; wrappers for Wire */ 184 | boolean Trill::requestRawData(uint8_t max_length) { 185 | uint8_t length = 0; 186 | 187 | prepareForDataRead(); 188 | 189 | if(max_length == 0xFF) { 190 | length = RAW_LENGTH; 191 | } 192 | if(length > kRawLength) 193 | length = kRawLength; 194 | 195 | /* The raw data might be longer than the Wire.h maximum buffer 196 | * (BUFFER_LENGTH in Wire.h). 197 | * If so, split it into two reads. */ 198 | if(length <= BUFFER_LENGTH) { 199 | wire_->requestFrom(i2c_address_, length); 200 | raw_bytes_left_ = 0; 201 | } 202 | else { 203 | int ret = wire_->requestFrom(i2c_address_, (uint8_t)BUFFER_LENGTH); 204 | if(ret > 0) 205 | raw_bytes_left_ = length - ret; 206 | else { 207 | // failed transmission. Device died? 208 | raw_bytes_left_ = 0; 209 | return false; 210 | } 211 | } 212 | return true; 213 | } 214 | 215 | int Trill::rawDataAvailable() { 216 | /* Raw data items are 2 bytes long; return number of them available */ 217 | return ((wire_->available() + raw_bytes_left_) >> 1); 218 | } 219 | 220 | /* Raw data is in 16-bit big-endian format */ 221 | int Trill::rawDataRead() { 222 | 223 | if(wire_->available() < 2) { 224 | /* Read more bytes if we need it */ 225 | if(raw_bytes_left_ > 0) { 226 | /* Move read pointer on device */ 227 | wire_->beginTransmission(i2c_address_); 228 | wire_->write(kOffsetData + BUFFER_LENGTH); 229 | wire_->endTransmission(); 230 | last_read_loc_ = kOffsetData + BUFFER_LENGTH; 231 | 232 | /* Now gather what's left */ 233 | wire_->requestFrom(i2c_address_, raw_bytes_left_); 234 | raw_bytes_left_ = 0; 235 | } 236 | 237 | /* Check again if we've got anything... */ 238 | if(wire_->available() < 2) 239 | return 0; 240 | } 241 | 242 | int result = ((uint8_t)wire_->read()) << 8; 243 | result += (int)wire_->read(); 244 | return result; 245 | } 246 | 247 | /* Scan configuration settings */ 248 | void Trill::setMode(Mode mode) { 249 | wire_->beginTransmission(i2c_address_); 250 | wire_->write(kOffsetCommand); 251 | wire_->write(kCommandMode); 252 | wire_->write(mode); 253 | wire_->endTransmission(); 254 | 255 | mode_ = mode; 256 | last_read_loc_ = kOffsetCommand; 257 | num_touches = 0; 258 | } 259 | 260 | void Trill::setScanSettings(uint8_t speed, uint8_t num_bits) { 261 | if(speed > 3) 262 | speed = 3; 263 | if(num_bits < 9) 264 | num_bits = 9; 265 | if(num_bits > 16) 266 | num_bits = 16; 267 | wire_->beginTransmission(i2c_address_); 268 | wire_->write(kOffsetCommand); 269 | wire_->write(kCommandScanSettings); 270 | wire_->write(speed); 271 | wire_->write(num_bits); 272 | wire_->endTransmission(); 273 | 274 | last_read_loc_ = kOffsetCommand; 275 | } 276 | 277 | void Trill::setPrescaler(uint8_t prescaler) { 278 | wire_->beginTransmission(i2c_address_); 279 | wire_->write(kOffsetCommand); 280 | wire_->write(kCommandPrescaler); 281 | wire_->write(prescaler); 282 | wire_->endTransmission(); 283 | 284 | last_read_loc_ = kOffsetCommand; 285 | } 286 | 287 | void Trill::setNoiseThreshold(uint8_t threshold) { 288 | wire_->beginTransmission(i2c_address_); 289 | wire_->write(kOffsetCommand); 290 | wire_->write(kCommandNoiseThreshold); 291 | wire_->write(threshold); 292 | wire_->endTransmission(); 293 | 294 | last_read_loc_ = kOffsetCommand; 295 | } 296 | 297 | void Trill::setIDACValue(uint8_t value) { 298 | wire_->beginTransmission(i2c_address_); 299 | wire_->write(kOffsetCommand); 300 | wire_->write(kCommandIdac); 301 | wire_->write(value); 302 | wire_->endTransmission(); 303 | 304 | last_read_loc_ = kOffsetCommand; 305 | } 306 | 307 | void Trill::setMinimumTouchSize(uint16_t size) { 308 | wire_->beginTransmission(i2c_address_); 309 | wire_->write(kOffsetCommand); 310 | wire_->write(kCommandMinimumSize); 311 | wire_->write(size >> 8); 312 | wire_->write(size & 0xFF); 313 | wire_->endTransmission(); 314 | 315 | last_read_loc_ = kOffsetCommand; 316 | } 317 | 318 | void Trill::setAutoScanInterval(uint16_t interval) { 319 | wire_->beginTransmission(i2c_address_); 320 | wire_->write(kOffsetCommand); 321 | wire_->write(kCommandAutoScanInterval); 322 | wire_->write(interval >> 8); 323 | wire_->write(interval & 0xFF); 324 | wire_->endTransmission(); 325 | 326 | last_read_loc_ = kOffsetCommand; 327 | } 328 | 329 | /* Prepare the device to read data if it is not already prepared */ 330 | void Trill::prepareForDataRead() { 331 | if(last_read_loc_ != kOffsetData) { 332 | wire_->beginTransmission(i2c_address_); 333 | wire_->write(kOffsetData); 334 | wire_->endTransmission(); 335 | 336 | last_read_loc_ = kOffsetData; 337 | } 338 | } 339 | 340 | int Trill::getButtonValue(uint8_t button_num) 341 | { 342 | if(mode_ != CENTROID) 343 | return -1; 344 | if(button_num > 1) 345 | return -1; 346 | if(device_type_ != TRILL_RING) 347 | return -1; 348 | 349 | return buffer_[2 * MAX_TOUCH_1D_OR_2D + button_num]; 350 | } 351 | 352 | unsigned int Trill::getNumChannels() 353 | { 354 | switch(device_type_) { 355 | case TRILL_BAR: return kNumChannelsBar; 356 | case TRILL_RING: return kNumChannelsRing; 357 | default: return kNumChannelsMax; 358 | } 359 | } 360 | 361 | bool Trill::is1D() 362 | { 363 | if(CENTROID != mode_) 364 | return false; 365 | switch(device_type_) { 366 | case TRILL_BAR: 367 | case TRILL_RING: 368 | case TRILL_CRAFT: 369 | case TRILL_FLEX: 370 | return true; 371 | default: 372 | return false; 373 | } 374 | } 375 | 376 | bool Trill::is2D() 377 | { 378 | if(CENTROID != mode_) 379 | return false; 380 | switch(device_type_) { 381 | case TRILL_SQUARE: 382 | case TRILL_HEX: 383 | return true; 384 | default: 385 | return false; 386 | } 387 | } 388 | 389 | uint8_t Touches::getNumTouches() const 390 | { 391 | return num_touches; 392 | } 393 | 394 | int Touches::touchLocation(uint8_t touch_num) const 395 | { 396 | if(touch_num < num_touches) 397 | return centroids[touch_num]; 398 | else 399 | return -1; 400 | } 401 | 402 | int Touches::touchSize(uint8_t touch_num) const 403 | { 404 | if(touch_num < num_touches) 405 | return sizes[touch_num]; 406 | else 407 | return -1; 408 | } 409 | 410 | unsigned int Touches2D::getNumHorizontalTouches() { 411 | return horizontal.getNumTouches(); 412 | } 413 | 414 | void Touches::processCentroids(uint8_t maxCentroids) { 415 | // Look for 1st instance of 0xFFFF (no touch) in the buffer 416 | for(num_touches = 0; num_touches < maxCentroids; ++num_touches) 417 | { 418 | if(0xffff == centroids[num_touches]) 419 | break;// at the first non-touch, break 420 | } 421 | // now num_touches is the number of active touches in the array 422 | } 423 | 424 | /* These methods for horizontal touches on 2D sliders */ 425 | int Touches2D::touchHorizontalLocation(uint8_t touch_num) { 426 | return horizontal.touchLocation(touch_num); 427 | } 428 | 429 | int Touches2D::touchHorizontalSize(uint8_t touch_num) { 430 | return horizontal.touchSize(touch_num); 431 | } 432 | --------------------------------------------------------------------------------