├── .gitignore ├── examples ├── trill-osc │ ├── node │ │ ├── package.json │ │ └── trill-osc.js │ ├── pd │ │ └── trill-osc.pd │ ├── udp.hh │ ├── trill-osc.cpp │ └── oscpkt.hh ├── Makefile ├── detect-trills │ └── detect-trills.cpp ├── general-print │ └── general-print.cpp └── general-settings │ └── general-settings.cpp ├── lib ├── processCentroids.h ├── CentroidDetection.h ├── I2c.h ├── calculateCentroids.h ├── CentroidDetection.cpp ├── Trill.h └── Trill.cpp └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | # the next three lines are to ignore files without extension (i.e.: binaries) in linux/examples/*/ 2 | examples/*/* 3 | !examples/*/*.* 4 | !examples/*/*/ 5 | 6 | *.o 7 | .*.sw? 8 | node_modules/ 9 | -------------------------------------------------------------------------------- /examples/trill-osc/node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "osc-node", 3 | "version": "1.0.0", 4 | "description": "", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "osc-min": "^1.1.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /examples/Makefile: -------------------------------------------------------------------------------- 1 | ##Usage 2 | ## make 3 | ##or specify PROJECT=, e.g.: 4 | ## make PROJECT=Trill_print 5 | ## 6 | 7 | LIB_DIR := ../lib/ 8 | CPPFLAGS := -I$(LIB_DIR) 9 | CXXFLAGS := -g -std=c++11 -Wno-psabi -Wno-unknown-warning-option 10 | CFLAGS := $(CXXFLAGS) 11 | 12 | CC := $(CXX) # ensure CXX is used for linking 13 | 14 | LIB_SRCS = $(wildcard $(LIB_DIR)/*.cpp) 15 | LIB_OBJS := $(LIB_SRCS:.cpp=.o) 16 | ALL_PROJECTS := $(shell find * -maxdepth 1 -type d) 17 | 18 | PROJECT_BIN := $(PROJECT)/$(PROJECT) 19 | PROJECT_SRCS := $(wildcard $(PROJECT)/*.cpp) 20 | PROJECT_OBJS := $(PROJECT_SRCS:%.cpp=%.o) 21 | 22 | ifneq ($(PROJECT),) 23 | $(PROJECT_BIN): $(PROJECT_OBJS) $(LIB_OBJS) 24 | endif 25 | 26 | all: ## Build all examples or the one specified as PROJECT= 27 | for project in $(ALL_PROJECTS); do $(MAKE) --no-print-directory PROJECT=$$project || exit 1; done 28 | 29 | clean: ## Clean al build files 30 | rm -rf */*.o $(LIB_DIR)/*.o 31 | 32 | help: ## Show this help 33 | @fgrep -h "##" $(MAKEFILE_LIST) | fgrep -v fgrep | sed -e 's/^\(.*\): .*##\(.*\)/\1:#\2/' | sed -e 's/^\(.*\)= .* -- \(.*\)/\1=#\2/' | sed 's/^##//' | awk -F"#" '{ printf "%-18s %-1s\n", $$1, $$2}' 34 | @echo '(default: $(.DEFAULT_GOAL))' 35 | 36 | ifneq (,$(PROJECT)) 37 | run: $(PROJECT_BIN) 38 | ./$(PROJECT_BIN) $(CL) 39 | endif 40 | -------------------------------------------------------------------------------- /lib/processCentroids.h: -------------------------------------------------------------------------------- 1 | temp = calculateCentroids(wVCentroid, wVCentroidSize, MAX_NUM_CENTROIDS, FIRST_SENSOR_V, LAST_SENSOR_V, numSensors); // Vertical centroids 2 | firstActiveSensor = temp & 0xFF; 3 | lastActiveSensor = temp >> 8; 4 | bActivityDetected = lastActiveSensor >= 0; 5 | 6 | temp = lastActiveSensor - (LAST_SENSOR_V - FIRST_SENSOR_V );// retrieve the (wrapped) index 7 | //check for activity in the wraparound area 8 | // IF the last centroid ended after wrapping around ... 9 | // AND the first centroid was located before the end of the last ... 10 | if(lastActiveSensor >= LAST_SENSOR_V - FIRST_SENSOR_V 11 | && (((BYTE)temp) << SLIDER_BITS) >= wVCentroid[0] ) 12 | { 13 | // THEN the last touch is used to replace the first one 14 | for(counter = MAX_NUM_CENTROIDS - 1; counter >= 1; counter--) { 15 | if(0xFFFF == wVCentroid[counter]) 16 | continue; 17 | // replace the first centroid 18 | wVCentroidSize[0] = wVCentroidSize[counter]; 19 | wVCentroid[0] = wVCentroid[counter]; 20 | // wrap around the position if needed 21 | if(wVCentroid[0] >= posEndOfLoop) 22 | wVCentroid[0] -= posEndOfLoop; 23 | // discard the last centroid 24 | wVCentroid[counter] = 0xFFFF; 25 | wVCentroidSize[counter] = 0x0; 26 | break; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /examples/detect-trills/detect-trills.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ____ _____ _ _ 3 | | __ )| ____| | / \ 4 | | _ \| _| | | / _ \ 5 | | |_) | |___| |___ / ___ \ 6 | |____/|_____|_____/_/ \_\ 7 | http://bela.io 8 | */ 9 | 10 | const char* helpText = 11 | "Scan the I2C bus for Trill devices\n" 12 | " Usage: %s \n" 13 | " : the bus to scan. Default is 1 (i.e.: /dev/i2c-1)\n" 14 | "======================\n" 15 | "\n" 16 | "This project is a handy utility which will identify all connected\n" 17 | "Trill devices and print information on them to the console\n" 18 | "\n" 19 | "This is particularly useful if you are unsure of the address of the sensor after\n" 20 | "changing it via the solder bridges on the back. This example will also give\n" 21 | "you a total count of the amount of Trill sensors currently connected on the\n" 22 | "specified bus.\n" 23 | "\n" 24 | "NOTE: as this program scans several addresses on the i2c bus\n" 25 | "it could cause non-Trill peripherals connected to it to malfunction.\n"; 26 | 27 | #include 28 | #include 29 | 30 | int main(int argc, char** argv) 31 | { 32 | unsigned int i2cBus = 1; 33 | if(argc >= 2) 34 | { 35 | if(std::string("--help") == std::string(argv[1])) { 36 | printf(helpText, argv[0]); 37 | return 0; 38 | } 39 | i2cBus = atoi(argv[1]); 40 | } 41 | printf("Trill devices detected on bus %d\n", i2cBus); 42 | printf("Address | Type\n"); 43 | unsigned int total = 0; 44 | for(uint8_t n = 0x20; n <= 0x50; ++n) { 45 | Trill::Device device = Trill::probe(i2cBus, n); 46 | if(device != Trill::NONE) { 47 | printf("%#4x (%3d) | %s\n", n, n, Trill::getNameFromDevice(device).c_str()); 48 | ++total; 49 | } 50 | } 51 | printf("\nTotal: %d devices\n", total); 52 | return 0; 53 | } 54 | -------------------------------------------------------------------------------- /lib/CentroidDetection.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | 6 | class CentroidDetection 7 | { 8 | public: 9 | typedef float DATA_T; 10 | CentroidDetection() {}; 11 | CentroidDetection(unsigned int numReadings, unsigned int maxNumCentroids, float sizeScale); 12 | CentroidDetection(const std::vector& order, unsigned int maxNumCentroids, float sizeScale); 13 | int setup(unsigned int numReadings, unsigned int maxNumCentroids, float sizeScale); 14 | int setup(const std::vector& order, unsigned int maxNumCentroids, float sizeScale); 15 | void process(const DATA_T* rawData); 16 | void setSizeScale(float sizeScale); 17 | void setMinimumTouchSize(DATA_T minSize); 18 | void setNoiseThreshold(DATA_T threshold); 19 | /** 20 | * Set how many of the values at the beginning of `rawData` can be 21 | * joined in a single centroid with those at the end of `rawData` if a 22 | * centroid is detected across them. 23 | * Useful for ring-like devices. 24 | */ 25 | void setWrapAround(unsigned int n); 26 | /** 27 | * How many extra bits to use during the fixed-point multiplication 28 | * that computes the centroids position. Defaults to 7. 29 | */ 30 | void setMultiplierBits(unsigned int n); 31 | unsigned int getNumTouches() const; 32 | DATA_T touchLocation(unsigned int touch_num) const; 33 | DATA_T touchSize(unsigned int touch_num) const; 34 | DATA_T compoundTouchLocation() const; 35 | DATA_T compoundTouchSize() const; 36 | private: 37 | typedef uint16_t WORD; 38 | class CalculateCentroids; 39 | std::vector centroids; 40 | std::vector sizes; 41 | std::vector centroidBuffer; 42 | std::vector sizeBuffer; 43 | unsigned int maxNumCentroids; 44 | std::vector order; 45 | unsigned int num_sensors; 46 | std::vector data; 47 | float sizeScale; 48 | float locationScale; 49 | std::shared_ptr cc; 50 | unsigned int num_touches; 51 | DATA_T noiseThreshold; 52 | }; 53 | -------------------------------------------------------------------------------- /lib/I2c.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | // heuristic to guess what version of i2c-dev.h we have: 8 | // the one installed with `apt-get install libi2c-dev` 9 | // would conflict with linux/i2c.h, while the stock 10 | // one requires linus/i2c.h 11 | #ifndef I2C_SMBUS_BLOCK_MAX 12 | // If this is not defined, we have the "stock" i2c-dev.h 13 | // so we include linux/i2c.h 14 | #include 15 | typedef unsigned char i2c_char_t; 16 | #else 17 | typedef char i2c_char_t; 18 | #endif 19 | #include 20 | 21 | #define MAX_BUF_NAME 64 22 | 23 | class I2c 24 | { 25 | 26 | protected: 27 | int i2C_bus; 28 | int i2C_address; 29 | int i2C_file; 30 | ssize_t readBytes(void* buf, size_t count); 31 | ssize_t writeBytes(const void* buf, size_t count); 32 | 33 | public: 34 | I2c(){}; 35 | I2c(I2c&&) = delete; 36 | int initI2C_RW(int bus, int address, int file); 37 | int closeI2C(); 38 | 39 | virtual ~I2c(); 40 | 41 | }; 42 | 43 | 44 | inline int I2c::initI2C_RW(int bus, int address, int fileHnd) 45 | { 46 | i2C_bus = bus; 47 | i2C_address = address; 48 | i2C_file = fileHnd; 49 | 50 | // open I2C device as a file 51 | char namebuf[MAX_BUF_NAME]; 52 | snprintf(namebuf, sizeof(namebuf), "/dev/i2c-%d", i2C_bus); 53 | 54 | if ((i2C_file = open(namebuf, O_RDWR)) < 0) 55 | { 56 | fprintf(stderr, "Failed to open %s I2C Bus\n", namebuf); 57 | return(1); 58 | } 59 | 60 | // target device as slave 61 | if (ioctl(i2C_file, I2C_SLAVE, i2C_address) < 0){ 62 | fprintf(stderr, "I2C_SLAVE address %#x failed...", i2C_address); 63 | return(2); 64 | } 65 | 66 | return 0; 67 | } 68 | 69 | 70 | 71 | inline int I2c::closeI2C() 72 | { 73 | if(i2C_file > 0) { 74 | if(close(i2C_file) > 0) 75 | return 1; 76 | else 77 | i2C_file = -1; 78 | } 79 | return 0; 80 | } 81 | 82 | inline ssize_t I2c::readBytes(void *buf, size_t count) 83 | { 84 | return read(i2C_file, buf, count); 85 | } 86 | 87 | inline ssize_t I2c::writeBytes(const void *buf, size_t count) 88 | { 89 | return write(i2C_file, buf, count); 90 | } 91 | 92 | inline I2c::~I2c(){} 93 | -------------------------------------------------------------------------------- /examples/general-print/general-print.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include 4 | const char* helpText = 5 | "Connect to one Trill device and print its readings to the console\n" 6 | " Usage: %s [
]\n" 7 | " is the bus that the device is connected to (i.e.: the X in /dev/i2c-X)\n" 8 | " is the name of the device (e.g.: `bar`, `square`,\n" 9 | " `craft`, `hex`, ring`, ...)\n" 10 | "
(optional) is the address of the device. If this is\n" 11 | " not passed, the default address for the specified device\n" 12 | " type will be used instead.\n" 13 | ; 14 | int gShouldStop; 15 | 16 | void interrupt_handler(int var) 17 | { 18 | gShouldStop = true; 19 | } 20 | 21 | Trill touchSensor; 22 | 23 | int main(int argc, char** argv) 24 | { 25 | std::string deviceName; 26 | int i2cBus = -1; 27 | uint8_t address = 255; 28 | if(2 > argc) { 29 | printf(helpText, argv[0]); 30 | return 1; 31 | } 32 | for(unsigned int c = 1; c < argc; ++c) 33 | { 34 | if(std::string("--help") == std::string(argv[c])) { 35 | printf(helpText, argv[0]); 36 | return 0; 37 | } 38 | if(1 == c) { 39 | i2cBus = std::stoi(argv[c]); 40 | } else if(2 == c) { 41 | deviceName = argv[c]; 42 | } else if(3 == c) { 43 | address = std::stoi(argv[c]); 44 | if(!address) // if failed, try again as hex 45 | address = std::stoi(argv[c], 0, 16); 46 | } 47 | } 48 | if(i2cBus < 0) { 49 | fprintf(stderr, "No or invalid bus specified\n"); 50 | return 1; 51 | } 52 | Trill::Device device = Trill::getDeviceFromName(deviceName); 53 | if(Trill::UNKNOWN == device) { 54 | fprintf(stderr, "No or invalid device name specified: `%s`\n", deviceName.c_str()); 55 | return 1; 56 | } 57 | printf("Opening device %s on bus %d ", Trill::getNameFromDevice(device).c_str(), i2cBus); 58 | if(255 != address) 59 | printf("at address: %#4x(%d)", address, address); 60 | printf("\n"); 61 | 62 | if(touchSensor.setup(i2cBus, device, address)) 63 | { 64 | fprintf(stderr, "Error while initialising device\n"); 65 | return 1; 66 | } 67 | touchSensor.printDetails(); 68 | signal(SIGINT, interrupt_handler); 69 | while(!gShouldStop) { 70 | touchSensor.readI2C(); 71 | // we print the sensor readings depending on the device mode, 72 | // so that if you change the device type above, you get meaningful 73 | // values for your device 74 | if(Trill::CENTROID == touchSensor.getMode()) { 75 | printf("Touches: %d:", touchSensor.getNumTouches()); 76 | for(unsigned int i = 0; i < touchSensor.getNumTouches(); i++) { 77 | printf("%1.3f ", touchSensor.touchLocation(i)); 78 | if(touchSensor.is2D()) 79 | printf("%1.3f ", touchSensor.touchHorizontalLocation(i)); 80 | } 81 | } 82 | else { 83 | for(unsigned int i = 0; i < touchSensor.getNumChannels(); i++) 84 | printf("%1.3f ", touchSensor.rawData[i]); 85 | } 86 | printf("\n"); 87 | usleep(100000); 88 | } 89 | return 0; 90 | } 91 | -------------------------------------------------------------------------------- /examples/trill-osc/pd/trill-osc.pd: -------------------------------------------------------------------------------- 1 | #N canvas 164 0 1173 778 12; 2 | #X obj 70 457 oscformat; 3 | #X obj 30 485 oscparse; 4 | #X obj 30 509 route list; 5 | #X msg 17 216 listAll; 6 | #X obj 102 313 list; 7 | #X obj 97 379 list split 4; 8 | #X obj 97 410 list trim; 9 | #X msg 80 210 deleteAll; 10 | #X obj 226 682 netsend -u -b; 11 | #X obj 158 615 list prepend send; 12 | #X obj 159 651 list trim; 13 | #X obj 314 715 oscparse; 14 | #X msg 28 120 connect 192.168.7.2 7562 7563; 15 | #X msg 330 607 disconnect; 16 | #X obj 178 430 list; 17 | #X obj 69 279 t b b a; 18 | #X msg 172 214 createAll 1; 19 | #X obj 103 343 list prepend set trill command; 20 | #X msg 670 285 readI2C mycraft; 21 | #X obj 268 188 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 22 | 1; 23 | #X msg 268 215 autoReadAll \$1; 24 | #X obj 315 744 route list; 25 | #X obj 470 622 print response; 26 | #X obj 485 550 route commandreply readings; 27 | #X obj 581 701 print reading; 28 | #X obj 487 521 route trill; 29 | #X obj 30 533 print ================sent; 30 | #X obj 864 273 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 0 31 | 1; 32 | #X text 89 162 Global commands; 33 | #X text 644 151 Create/delete; 34 | #X text 664 120 Instance commands; 35 | #X text 682 265 Read \, once or repeatedly; 36 | #X text 487 594 Responses to commands; 37 | #X text 594 673 Readings; 38 | #X msg 379 223 loopSleep \$1; 39 | #X floatatom 378 195 5 0 0 0 - - -; 40 | #X text 374 172 ms between auto readings; 41 | #X text 650 165 first argument is \, which is to be used in any 42 | other instance command; 43 | #X text 665 337 These are explained at; 44 | #X text 672 352 https://learn.bela.io/products/trill/integrating-trill-into-your-projects/ 45 | ; 46 | #X msg 652 239 delete mytrill; 47 | #X msg 639 216 new mytrill 1 craft 56; 48 | #X msg 864 294 autoRead mytrill \$1; 49 | #X msg 495 344 updateBaseline mytrill; 50 | #X msg 495 365 setMode mytrill RAW; 51 | #X msg 497 387 setScanSettings mytrill 0 12; 52 | #X msg 497 410 setPrescaler mytrill 4; 53 | #X msg 498 430 setNoiseThreshold mytrill 0.0625; 54 | #X obj 691 652 print other; 55 | #X text 644 193 new ; 56 | #X text 21 14 Trill + OSC + Puredata example; 57 | #X text 25 94 Adjust IP and ports as needed:; 58 | #X text 299 10 Note: if you are on Bela \, it is more efficient to 59 | use the; 60 | #X text 298 22 Trill/*-pd examples instead; 61 | #X text 17 61 Start the trill-osc program and make sure it is; 62 | #X text 18 75 listening on port 7562; 63 | #X connect 0 0 1 0; 64 | #X connect 0 0 9 0; 65 | #X connect 1 0 2 0; 66 | #X connect 2 0 26 0; 67 | #X connect 3 0 15 0; 68 | #X connect 4 0 17 0; 69 | #X connect 5 0 6 0; 70 | #X connect 5 1 14 1; 71 | #X connect 6 0 0 0; 72 | #X connect 7 0 15 0; 73 | #X connect 8 1 11 0; 74 | #X connect 9 0 10 0; 75 | #X connect 10 0 8 0; 76 | #X connect 11 0 21 0; 77 | #X connect 12 0 8 0; 78 | #X connect 13 0 8 0; 79 | #X connect 14 0 0 0; 80 | #X connect 15 1 14 0; 81 | #X connect 15 2 4 0; 82 | #X connect 16 0 15 0; 83 | #X connect 17 0 5 0; 84 | #X connect 18 0 15 0; 85 | #X connect 19 0 20 0; 86 | #X connect 20 0 15 0; 87 | #X connect 21 0 25 0; 88 | #X connect 21 1 48 0; 89 | #X connect 23 0 22 0; 90 | #X connect 23 1 24 0; 91 | #X connect 23 2 48 0; 92 | #X connect 25 0 23 0; 93 | #X connect 25 1 48 0; 94 | #X connect 27 0 42 0; 95 | #X connect 34 0 15 0; 96 | #X connect 35 0 34 0; 97 | #X connect 40 0 15 0; 98 | #X connect 41 0 15 0; 99 | #X connect 42 0 15 0; 100 | #X connect 43 0 15 0; 101 | #X connect 44 0 15 0; 102 | #X connect 45 0 15 0; 103 | #X connect 46 0 15 0; 104 | #X connect 47 0 15 0; 105 | -------------------------------------------------------------------------------- /examples/general-settings/general-settings.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | const char* helpText = 4 | "Connect to one Trill device, set it to RAW mode, change some settings and\n" 5 | "print its readings to the console\n" 6 | " Usage: %s [
]\n" 7 | " is the bus that the device is connected to (i.e.: the X in /dev/i2c-X)\n" 8 | " is the name of the device (e.g.: `bar`, `square`,\n" 9 | " `craft`, `hex`, ring`, ...)\n" 10 | "
(optional) is the address of the device. If this is\n" 11 | " not passed, the default address for the specified device\n" 12 | " type will be used instead.\n" 13 | ; 14 | 15 | Trill touchSensor; 16 | 17 | int gShouldStop; 18 | 19 | void interrupt_handler(int var) 20 | { 21 | gShouldStop = true; 22 | } 23 | 24 | int main(int argc, char** argv) 25 | { 26 | std::string deviceName; 27 | int i2cBus = -1; 28 | uint8_t address = 255; 29 | if(2 > argc) { 30 | printf(helpText, argv[0]); 31 | return 1; 32 | } 33 | for(unsigned int c = 1; c < argc; ++c) 34 | { 35 | if(std::string("--help") == std::string(argv[c])) { 36 | printf(helpText, argv[0]); 37 | return 0; 38 | } 39 | if(1 == c) { 40 | i2cBus = std::stoi(argv[c]); 41 | } else if(2 == c) { 42 | deviceName = argv[c]; 43 | } else if(3 == c) { 44 | address = std::stoi(argv[c]); 45 | if(!address) // if failed, try again as hex 46 | address = std::stoi(argv[c], 0, 16); 47 | } 48 | } 49 | if(i2cBus < 0) { 50 | fprintf(stderr, "No or invalid bus specified\n"); 51 | return 1; 52 | } 53 | Trill::Device device = Trill::getDeviceFromName(deviceName); 54 | if(Trill::UNKNOWN == device) { 55 | fprintf(stderr, "No or invalid device name specified: `%s`\n", deviceName.c_str()); 56 | return 1; 57 | } 58 | printf("Opening device %s on bus %d ", Trill::getNameFromDevice(device).c_str(), i2cBus); 59 | if(255 != address) 60 | printf("at address: %#4x(%d)", address, address); 61 | printf("\n"); 62 | 63 | if(touchSensor.setup(i2cBus, device, address)) 64 | { 65 | fprintf(stderr, "Error while initialising device\n"); 66 | return 1; 67 | } 68 | touchSensor.printDetails(); 69 | 70 | if(touchSensor.setMode(Trill::RAW) == 0) { 71 | printf("Operation mode set to %s.\n", Trill::getNameFromMode(Trill::RAW)); 72 | } else { 73 | fprintf(stderr, "Communication error\n"); 74 | return 1; 75 | } 76 | 77 | unsigned int newSpeed = 0; 78 | if(touchSensor.setScanSettings(newSpeed) == 0) { 79 | printf("Scan speed set to %d.\n", newSpeed); 80 | } else { 81 | fprintf(stderr, "Communication error\n"); 82 | return 1; 83 | } 84 | 85 | unsigned int newPrescaler = 3; 86 | if(touchSensor.setPrescaler(newPrescaler) == 0) { 87 | printf("Prescaler set to %d.\n", newPrescaler); 88 | } else { 89 | fprintf(stderr, "Communication error\n"); 90 | return 1; 91 | } 92 | 93 | float newThreshold = 0.01; 94 | if(touchSensor.setNoiseThreshold(newThreshold) == 0) { 95 | printf("Threshold set to %d.\n", newThreshold); 96 | } else { 97 | fprintf(stderr, "Communication error\n"); 98 | return 1; 99 | } 100 | 101 | if(touchSensor.updateBaseline() != 0) { 102 | fprintf(stderr, "Communication error\n"); 103 | return 1; 104 | } 105 | 106 | signal(SIGINT, interrupt_handler); 107 | while(!gShouldStop) 108 | { 109 | touchSensor.readI2C(); 110 | printf("raw: "); 111 | for(unsigned int i = 0; i < touchSensor.getNumChannels(); i++) 112 | printf("%1.3f ", touchSensor.rawData[i]); 113 | printf("\n"); 114 | usleep(100000); 115 | } 116 | return 0; 117 | } 118 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Trill: Touch Sensing for Makers 2 | 3 | Trill sensors are designed to 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 | This repo contains the Trill library and examples for Linux systems, such as Raspberry Pi. 8 | 9 | For other platforms: 10 | 11 | - Bela: the Bela IDE comes pre-loaded with the [Trill library](https://github.com/BelaPlatform/Bela/tree/master/libraries/Trill) and [Trill examples](https://github.com/BelaPlatform/Bela/tree/master/examples/Trill). 12 | 13 | - [`Trill-Arduino`](https://github.com/BelaPlatform/Trill-Arduino) contains the Trill library and examples for Arduino, as well as Arduino-compatible systems such as Teensy. These also include examples of how to connect your Trill sensor to Processing to generate visuals. 14 | 15 | ## Trill hardware designs 16 | 17 | - the [`Trill`](github.com/BelaPlatform/Trill/) repo contains the editable PCB designs and manufacturing files for Trill sensors. 18 | 19 | ### Licensing: PCB designs 20 | 21 | All Trill hardware design files are licensed under a [Creative Commons Attribution Sharealike 3.0 license (or CC-BY-SA)](http://creativecommons.org/licenses/by-sa/3.0/), meaning that you can freely use these for your own projects provided you supply attribution and a link to this repo, and release your own design files under the same licesnse. [Read more about CC-BY-SA on the Creative Commons site](http://creativecommons.org/licenses/by-sa/3.0/). 22 | 23 | ### Licensing: Firmware 24 | 25 | Trill firmware is the code that's flashed to each Trill sensor, and this firmware is also provided here. Trill firmware is licensed under the [Gnu Public License, or GPL](https://www.gnu.org/licenses/gpl-3.0.en.html). This means that you can use this code for any purpose, but you must push the changes you make back to this repo. 26 | 27 | ### Licensing: Commercial 28 | 29 | It's possible to use Trill design files and firmware for a commercial projects free of charge under [CC-BY-SA](http://creativecommons.org/licenses/by-sa/3.0/) (hardware) and [GPL](https://www.gnu.org/licenses/gpl-3.0.en.html) (firmware). Both licenses allow you to use these assets yourself under the license requirements (which include releasing any changes you make to the designs under the same license). 30 | 31 | If you want to use Trill design files *but not provide attribution and/or not release your source code* - for example, because you want to create something to sell using these files but don't want to make your source files public and linked to this repo - this is still possible, but requires a commercial license. Bela can provide you with a commercial licesnse that fits your project and its scope, starting at £500 for 500 pieces. Get in touch with the Bela team at info@bela.io to discuss your product and the license that's right for you. 32 | 33 | Please note: The above commercial licensing applies only to modifications to our provided firmware code and PCB designs. You are free to buy Trill sensors for use in any commercial or personal project. We offer discounts for bulk orders of Trill sensors for commercial use; please email info@bela.io to discuss your needs. 34 | 35 | ## Trill name and logo 36 | 37 | The Trill name and logo are copyright Augmented Instruments Ltd. Any modified designs that you release should not use the Trill name or logo. 38 | 39 | ## Datasheet 40 | 41 | The [`Trill`](github.com/BelaPlatform/Trill/) repo contains the Trill datasheet. There is one datasheet for all Trill sensors that includes technical diagrams and usage information. 42 | 43 | ## Fritzing files 44 | 45 | Fritzing parts are available for all Trill sensors, in the [Bela Fritzing repo](https://github.com/BelaPlatform/fritzing). 46 | 47 | ## Learn more 48 | 49 | The Bela Knowledge Base contains great documentation on using Trill! Visit [learn.bela.io/trill](https://learn.bela.io/trill) for more information, as well as our Get Started Guide. 50 | -------------------------------------------------------------------------------- /lib/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 | -------------------------------------------------------------------------------- /lib/CentroidDetection.cpp: -------------------------------------------------------------------------------- 1 | #include "CentroidDetection.h" 2 | 3 | // a small helper class, whose main purpose is to wrap the #include 4 | // and make all the variables related to it private and multi-instance safe 5 | class CentroidDetection::CalculateCentroids 6 | { 7 | public: 8 | typedef CentroidDetection::WORD WORD; 9 | typedef uint8_t BYTE; 10 | typedef uint8_t BOOL; 11 | WORD* CSD_waSnsDiff; 12 | WORD wMinimumCentroidSize = 0; 13 | BYTE SLIDER_BITS = 7; 14 | WORD wAdjacentCentroidNoiseThreshold = 400; // Trough between peaks needed to identify two centroids 15 | // calculateCentroids is defined here: 16 | #include "calculateCentroids.h" 17 | void processCentroids(WORD *wVCentroid, WORD *wVCentroidSize, BYTE MAX_NUM_CENTROIDS, BYTE FIRST_SENSOR_V, BYTE LAST_SENSOR_V, BYTE numSensors) { 18 | long temp; 19 | signed char firstActiveSensor; 20 | signed char lastActiveSensor; 21 | BOOL bActivityDetected; 22 | BYTE counter; 23 | WORD posEndOfLoop = (LAST_SENSOR_V - FIRST_SENSOR_V) << SLIDER_BITS; 24 | #include "processCentroids.h" 25 | } 26 | }; 27 | 28 | CentroidDetection::CentroidDetection(unsigned int numReadings, unsigned int maxNumCentroids, float sizeScale) 29 | { 30 | setup(numReadings, maxNumCentroids, sizeScale); 31 | } 32 | 33 | CentroidDetection::CentroidDetection(const std::vector& order, unsigned int maxNumCentroids, float sizeScale) 34 | { 35 | setup(order, maxNumCentroids, sizeScale); 36 | } 37 | 38 | int CentroidDetection::setup(unsigned int numReadings, unsigned int maxNumCentroids, float sizeScale) 39 | { 40 | std::vector order; 41 | for(unsigned int n = 0; n < numReadings; ++n) 42 | order.push_back(n); 43 | return setup(order, maxNumCentroids, sizeScale); 44 | } 45 | 46 | int CentroidDetection::setup(const std::vector& order, unsigned int maxNumCentroids, float sizeScale) 47 | { 48 | this->order = order; 49 | setWrapAround(0); 50 | this->maxNumCentroids = maxNumCentroids; 51 | centroidBuffer.resize(maxNumCentroids); 52 | sizeBuffer.resize(maxNumCentroids); 53 | centroids.resize(maxNumCentroids); 54 | sizes.resize(maxNumCentroids); 55 | data.resize(order.size()); 56 | setSizeScale(sizeScale); 57 | setNoiseThreshold(0); 58 | cc = std::shared_ptr(new CalculateCentroids()); 59 | setMultiplierBits(cc->SLIDER_BITS); 60 | num_touches = 0; 61 | return 0; 62 | } 63 | 64 | void CentroidDetection::setWrapAround(unsigned int n) 65 | { 66 | num_sensors = order.size() + n; 67 | } 68 | 69 | void CentroidDetection::setMultiplierBits(unsigned int n) 70 | { 71 | cc->SLIDER_BITS = n; 72 | locationScale = (order.size() - 1) * (1 << cc->SLIDER_BITS); 73 | } 74 | 75 | void CentroidDetection::process(const DATA_T* rawData) 76 | { 77 | for(unsigned int n = 0; n < order.size(); ++n) 78 | { 79 | float val = rawData[order[n]] * (1 << 12); 80 | val -= noiseThreshold; 81 | if(val < 0) 82 | val = 0; 83 | data[n] = val; 84 | } 85 | cc->CSD_waSnsDiff = data.data(); 86 | cc->processCentroids(centroidBuffer.data(), sizeBuffer.data(), maxNumCentroids, 0, order.size(), num_sensors); 87 | 88 | unsigned int locations = 0; 89 | // Look for 1st instance of 0xFFFF (no touch) in the buffer 90 | unsigned int i; 91 | for(i = 0; i < centroidBuffer.size(); ++i) 92 | { 93 | if(0xffff == centroidBuffer[i]) 94 | break;// at the first non-touch, break 95 | centroids[i] = centroidBuffer[i] / locationScale; 96 | sizes[i] = sizeBuffer[i] / sizeScale; 97 | } 98 | num_touches = i; 99 | } 100 | 101 | void CentroidDetection::setSizeScale(float sizeScale) 102 | { 103 | this->sizeScale = sizeScale; 104 | } 105 | 106 | void CentroidDetection::setMinimumTouchSize(DATA_T minSize) 107 | { 108 | cc->wMinimumCentroidSize = minSize; 109 | } 110 | 111 | void CentroidDetection::setNoiseThreshold(DATA_T threshold) 112 | { 113 | noiseThreshold = threshold; 114 | } 115 | 116 | unsigned int CentroidDetection::getNumTouches() const 117 | { 118 | return num_touches; 119 | } 120 | 121 | CentroidDetection::DATA_T CentroidDetection::touchLocation(unsigned int touch_num) const 122 | { 123 | if(touch_num < maxNumCentroids) 124 | return centroids[touch_num]; 125 | else 126 | return 0; 127 | } 128 | 129 | CentroidDetection::DATA_T CentroidDetection::touchSize(unsigned int touch_num) const 130 | { 131 | if(touch_num < num_touches) 132 | return sizes[touch_num]; 133 | else 134 | return 0; 135 | } 136 | 137 | // code below from Trill.cpp 138 | 139 | #define compoundTouch(LOCATION, SIZE, TOUCHES) {\ 140 | float avg = 0;\ 141 | float totalSize = 0;\ 142 | unsigned int numTouches = TOUCHES;\ 143 | for(unsigned int i = 0; i < numTouches; i++) {\ 144 | avg += LOCATION(i) * SIZE(i);\ 145 | totalSize += SIZE(i);\ 146 | }\ 147 | if(numTouches)\ 148 | avg = avg / totalSize;\ 149 | return avg;\ 150 | } 151 | 152 | CentroidDetection::DATA_T CentroidDetection::compoundTouchLocation() const 153 | { 154 | compoundTouch(touchLocation, touchSize, getNumTouches()); 155 | } 156 | 157 | CentroidDetection::DATA_T CentroidDetection::compoundTouchSize() const 158 | { 159 | float size = 0; 160 | for(unsigned int i = 0; i < getNumTouches(); i++) 161 | size += touchSize(i); 162 | return size; 163 | } 164 | -------------------------------------------------------------------------------- /examples/trill-osc/node/trill-osc.js: -------------------------------------------------------------------------------- 1 | var dgram = require('dgram'); 2 | var osc = require('osc-min'); 3 | // Default IPs and ports 4 | var localPort = 7563; 5 | var remoteIp = '127.0.0.1'; 6 | var remotePort = 7562; 7 | 8 | // hack to detect if we are running in the REPL or not 9 | // https://stackoverflow.com/questions/56403255/determine-if-javascript-nodejs-code-is-running-in-a-repl 10 | // not 100% accurate, e.g: for `node -i $($< ./FILE.js )`, it retunrs false 11 | var repl = false; 12 | try { 13 | __dirname; 14 | } catch (e) { 15 | repl = true; 16 | } 17 | 18 | var args = process.argv; 19 | if(args.length > 2){ 20 | if(args[2] == 'help'){ 21 | console.log([ 22 | "Usage: ", 23 | "`node trill-osc `", 24 | "All parameters are optional, defaults to:", 25 | "`node trill-osc "+remoteIp+" "+remotePort+" "+localPort+"`" 26 | ].join("\n")); 27 | exit(); 28 | } 29 | remoteIp = args[2]; 30 | if(args.length > 3) 31 | remotePort = args[3]; 32 | if(args.length > 4) 33 | localPort = args[4]; 34 | } 35 | function help() 36 | { 37 | //"HEREDOC" https://stackoverflow.com/a/56031250/2958741 38 | var str = (function(){/** 39 | A utility to talk to the trill-osc example. 40 | Send commands with 41 | sendTrillCommand("COMMAND", args ...) 42 | 43 | All commands are sent to "/trill/commands/" with 0 or more arguments. 44 | 45 | Global commands: 46 | list all enabled devices: 47 | sendTrillCommand("listAll") 48 | discover and create all Trill devices on the specified `i2cBus`: 49 | sendTrillCommand("createAll", i2cBus) 50 | delete all active Trill devices: 51 | sendTrillCommand("deleteAll") 52 | set whether all devices `should` read (and send) new data automatically or not: 53 | sendTrillCommand("autoReadAll", should) 54 | change the scanning rate so that there is a `ms` sleep in between reads (and sends): 55 | sendTrillCommand("loopSleep", ms) 56 | 57 | Instance commands: they all start with a string id 58 | 59 | create Trill device on the specified I2C `busNumber`, of the specified 60 | `deviceType`, at the specified `i2cAddress` (optional). 61 | `deviceType` is a string representing the name of the device (e.g.: "bar", "square", etc). 62 | Use `deviceType = "unknown"` for accepting any type, but then you have to 63 | specify a valid value for `i2cAddress`. 64 | sendTrillCommand("new", id, i2cBus, deviceType, i2cAddress) 65 | delete exising Trill device 66 | sendTrillCommand("delete", id) 67 | set whether device `should` read (and send) new data automatically (or not) 68 | sendTrillCommand("autoRead", id, should) 69 | ask the device to read (and send) data once 70 | sendTrillCommand("readI2C", id) 71 | 72 | More instance commands, which map directly to the C++ API http://docs.bela.io/classTrill.html 73 | sendTrillCommand("updateBaseline", id) 74 | sendTrillCommand("setPrescaler", id, value) 75 | sendTrillCommand("setNoiseThreshold", id, value) 76 | sendTrillCommand("setMode", id, mode) // mode is a string: "centroid", "raw", "baseline", or "diff" 77 | 78 | Incoming messages will be at: 79 | /trill/commandreply << for replies to commands 80 | 81 | Readings: 82 | 1D devices in centroid mode: 83 | /trill/readings//touches ... 84 | 2D devices in centroid mode (compoundTouch): 85 | /trill/readings//touchXY 86 | Devices in raw/baseline/diff mode: 87 | /trill/readings//raw ... 88 | /trill/readings//baseline ... 89 | /trill/readings//diff ... 90 | 91 | Disable print readings: 92 | set the global `logReadings` variable to true (default) or false if you want to 93 | disable/enable printing the readings to the node console. 94 | 95 | use help() to print this message again 96 | 97 | **/}).toString().slice(15,-5); 98 | console.log(str); 99 | } 100 | 101 | console.log("send to: "+remoteIp+":"+remotePort+", receive on: :"+localPort) 102 | 103 | // socket to send and receive OSC messages from bela 104 | var socket = dgram.createSocket('udp4'); 105 | socket.bind(localPort); 106 | 107 | socket.on('message', (message, info) => { 108 | 109 | var msg = osc.fromBuffer(message); 110 | 111 | if (msg.oscType === 'message'){ 112 | parseMessage(msg); 113 | } else if (msg.elements){ 114 | for (let element of msg.elements){ 115 | parseMessage(element); 116 | } 117 | } 118 | 119 | }); 120 | 121 | var logReadings = 1; 122 | var readingsAddressReg = /^\/trill\/readings.*/ 123 | function parseMessage(msg){ 124 | 125 | if(msg.address.match(readingsAddressReg) && !logReadings) 126 | return; 127 | var address = msg.address.split('/'); 128 | if (!address || !address.length || address.length <2){ 129 | console.log('bad OSC address', address); 130 | return; 131 | } 132 | 133 | console.log('address:', msg.address); 134 | for (let arg of msg.args){ 135 | console.log(arg); 136 | if (arg.type === 'blob'){ 137 | floats = []; 138 | for (let i = 0; i< arg.value.length; i += 4){ 139 | floats.push(arg.value.readFloatLE(i).toPrecision(5)); 140 | } 141 | console.log(floats); 142 | } 143 | } 144 | } 145 | 146 | function sendTrillCommand(command /*, arguments */){ 147 | var msg = ({ 148 | address : '/trill/command/' + command, 149 | args : [ ], 150 | }); 151 | var args = arguments; 152 | // start from n = 1 : skip first argument `command` 153 | for (let n = 1; n < args.length; ++n) { 154 | let type; 155 | let arg = args[n]; 156 | switch (typeof(arg)) 157 | { 158 | case "string": 159 | type = 'string'; 160 | break; 161 | case "number": 162 | type = 'float'; 163 | break; 164 | default: 165 | console.log("Unknown type", typeof(arg)); 166 | } 167 | if(type) 168 | msg.args.push({type: type, value: arg}); 169 | } 170 | //console.log("sending: ", msg); 171 | var buffer = osc.toBuffer(msg); 172 | socket.send(buffer, 0, buffer.length, remotePort, remoteIp, function(err) { 173 | if(err) 174 | console.log("Error wil sending ", buffer); 175 | }); 176 | } 177 | 178 | 179 | // command line interface 180 | help(); 181 | if(repl) { 182 | 183 | } else { 184 | cli(); 185 | } 186 | 187 | function cli() { 188 | const readline = require('readline'); 189 | 190 | const rl = readline.createInterface({ 191 | input: process.stdin, 192 | output: process.stdout 193 | }); 194 | console.log("This is an interactive console where your js is evaluated, but not a full REPL.\n"+ 195 | "You may consider running this script interactively in the node REPL (e.g.: running:\n"+ 196 | "`.load ./trill-osc.js` inside node."); 197 | 198 | function lineCb(line) { 199 | var str = line.trim(); 200 | if(str === "help") 201 | help(); 202 | else { 203 | try { 204 | eval(str); 205 | } catch (e) { 206 | console.log(e); 207 | } 208 | } 209 | process.stdout.write(">> "); 210 | rl.question('>> ', lineCb); 211 | } 212 | rl.question('>> ', lineCb); 213 | } 214 | -------------------------------------------------------------------------------- /examples/trill-osc/udp.hh: -------------------------------------------------------------------------------- 1 | /* 2 | This file provides a dumb c++ wrapper for sending OSC packets over UDP. 3 | */ 4 | 5 | /* Copyright (C) 2010 Julien Pommier 6 | 7 | This software is provided 'as-is', without any express or implied 8 | warranty. In no event will the authors be held liable for any damages 9 | arising from the use of this software. 10 | 11 | Permission is granted to anyone to use this software for any purpose, 12 | including commercial applications, and to alter it and redistribute it 13 | freely, subject to the following restrictions: 14 | 15 | 1. The origin of this software must not be misrepresented; you must not 16 | claim that you wrote the original software. If you use this software 17 | in a product, an acknowledgment in the product documentation would be 18 | appreciated but is not required. 19 | 2. Altered source versions must be plainly marked as such, and must not be 20 | misrepresented as being the original software. 21 | 3. This notice may not be removed or altered from any source distribution. 22 | 23 | (this is the zlib license) 24 | */ 25 | 26 | #ifndef OSCPKT_UDP_HH 27 | #define OSCPKT_UDP_HH 28 | 29 | #include 30 | #if defined(_MSC_VER) || defined(WIN32) 31 | /* 32 | if windows.h has been already included, be prepared for tons of 33 | compile errors. winsock2 must be included BEFORE windows.h . -- OR 34 | define WIN32_LEAN_AND_MEAN before the first #include to 35 | prevent it from including tons of crap (winsock.h etc) 36 | */ 37 | # include 38 | # include 39 | # include 40 | # if defined(_MSC_VER) 41 | # pragma comment(lib, "ws2_32.lib") 42 | # endif 43 | #elif defined(__APPLE__) /* Is recognized by Clang */ 44 | # include 45 | # include 46 | # include 47 | # include 48 | # include 49 | # include 50 | # include 51 | #else 52 | # include 53 | # include 54 | # include 55 | # include 56 | #endif 57 | #include 58 | #include 59 | #include 60 | #include 61 | #include 62 | #include 63 | #include 64 | 65 | namespace oscpkt { 66 | 67 | /** a wrapper class for holding an ip address, mostly used internnally */ 68 | class SockAddr { 69 | union { 70 | sockaddr_storage ss; // hold an IPv4 or IPv6 address 71 | struct sockaddr sa; 72 | } addr_; 73 | public: 74 | struct sockaddr &addr() { return addr_.sa; } 75 | const struct sockaddr &addr() const { return addr_.sa; } 76 | size_t maxLen() const { return sizeof addr_; } 77 | size_t actualLen() const { 78 | if (addr().sa_family == AF_UNSPEC) return 0; 79 | else if (addr().sa_family == AF_INET) return sizeof(struct sockaddr_in); 80 | else if (addr().sa_family == AF_INET6) return sizeof(struct sockaddr_in6); 81 | else return sizeof addr_; 82 | } 83 | 84 | SockAddr() { memset(&addr_, 0, sizeof addr_); } 85 | bool empty() const { return addr().sa_family == AF_UNSPEC; /* this is the 0 value */ } 86 | /** retrieve the current port number, -1 in case of error */ 87 | int getPort() const { 88 | char servname[512]; 89 | int err = getnameinfo(&addr_.sa, sizeof addr_, 0, 0, servname, sizeof servname, NI_NUMERICSERV); 90 | return (err == 0 ? atoi(servname) : -1); 91 | } 92 | /* convert to a string representation (ip:port) */ 93 | std::string asString() const { 94 | std::string s; 95 | if (addr().sa_family) { 96 | char hostname[512], servname[512]; 97 | int err = getnameinfo(&addr_.sa, 98 | sizeof addr_, hostname, sizeof hostname, servname, sizeof servname, NI_NUMERICHOST|NI_NUMERICSERV); 99 | if (err == 0) { 100 | s = hostname; s += ":"; s += servname; 101 | } 102 | } 103 | return s; 104 | } 105 | 106 | friend std::ostream &operator<<(std::ostream &os, const SockAddr &ip) { 107 | os << "["; 108 | switch (ip.addr().sa_family) { 109 | case AF_UNSPEC: os << "AF_UNSPEC"; break; 110 | case AF_INET: os << "IPv4"; break; 111 | case AF_INET6: os << "IPv6"; break; 112 | default: os << "unknown family '" << ip.addr().sa_family << "'"; break; 113 | } 114 | os << " " << ip.asString() << "]"; 115 | return os; 116 | } 117 | }; 118 | 119 | 120 | /** 121 | just a wrapper over the classical socket stuff 122 | 123 | should be robust, simple to use, IPv6 ready (avoids all deprecated 124 | stuff such as gethostbyname etc), and portable (mac/linux/windows) 125 | 126 | Try to avoid sending packets larger than 8192 because some other 127 | implementation may truncate them (python's DatagramRequestHandler 128 | of OSC.py for example). 129 | */ 130 | struct UdpSocket { 131 | std::string error_message; 132 | int handle; /* the file descriptor for the socket */ 133 | SockAddr local_addr /* initialised only for bound sockets */; 134 | SockAddr remote_addr; /* initialised for connected sockets. Also updated for bound sockets after each datagram received */ 135 | 136 | std::vector buffer; 137 | 138 | 139 | UdpSocket() : handle(-1) { 140 | #ifdef WIN32 141 | WSADATA wsa_data; 142 | if (WSAStartup(MAKEWORD(2,2), &wsa_data) != 0) { 143 | setErr("winsock failed to initialise"); 144 | } 145 | #endif 146 | } 147 | 148 | ~UdpSocket() { 149 | close(); 150 | #ifdef WIN32 151 | WSACleanup(); 152 | #endif 153 | } 154 | 155 | void close() { 156 | if (handle != -1) { 157 | #ifdef WIN32 158 | ::closesocket(handle); 159 | #else 160 | ::close(handle); 161 | #endif 162 | handle = -1; 163 | } 164 | } 165 | 166 | bool isOk() const { return error_message.empty(); } 167 | const std::string &errorMessage() const { return error_message; } 168 | 169 | bool isBound() const { return !local_addr.empty(); } 170 | int boundPort() const { return local_addr.getPort(); } 171 | std::string boundPortAsString() const { 172 | char s[512]; 173 | #ifndef _MSC_VER 174 | snprintf(s, 512, "%d", boundPort()); 175 | #else 176 | _snprintf_s(s,512,512, "%d", boundPort()); 177 | #endif 178 | return s; 179 | } 180 | int socketHandle() const { return handle; } 181 | std::string localHostName() const { 182 | /* this stuff is not very nice but this is what liblo does in order to 183 | find out a sensible name for the local host */ 184 | char hostname_buf[512]; 185 | if (gethostname(hostname_buf, sizeof hostname_buf) != 0) 186 | hostname_buf[0] = 0; 187 | hostname_buf[sizeof hostname_buf - 1] = 0; 188 | struct hostent * host = gethostbyname(hostname_buf); 189 | if (host) { return host->h_name; } 190 | return hostname_buf[0] ? hostname_buf : "localhost"; 191 | } 192 | std::string localHostNameWithPort() const { return (localHostName() + ":") + boundPortAsString(); } 193 | 194 | enum { OPTION_UNSPEC=0, OPTION_FORCE_IPV4=1, OPTION_FORCE_IPV6=2, 195 | OPTION_DEFAULT=OPTION_FORCE_IPV4 // according to liblo's README, using ipv6 sockets causes issues with other non-ipv6 enabled osc software 196 | }; 197 | 198 | /** open the socket and bind it to a port. Use this when you want to read 199 | incoming data on the specified port, using the function receiveNextDatagram. 200 | */ 201 | bool bindTo(int port, int options = OPTION_DEFAULT) { 202 | return openSocket("", port, options); 203 | } 204 | 205 | /** open the socket, and prepare for sending datagrams to the specified host:port */ 206 | bool connectTo(const std::string &host, const std::string &port, int options = OPTION_DEFAULT) { 207 | return openSocket(host, port, options); 208 | } 209 | bool connectTo(const std::string &host, int port, int options = OPTION_DEFAULT) { 210 | return openSocket(host, port, options); 211 | } 212 | 213 | void setErr(const std::string &msg) { 214 | if (error_message.empty()) error_message = msg; 215 | } 216 | 217 | /** wait for the next datagram to arrive on our bound socket. Return 218 | false in case of failure, or timeout. When the timeout_ms is set 219 | to -1, it will wait forever. 220 | 221 | The datagram is available with the getDatagramData() / getDatagramSize() functions, 222 | the sender address can be retrieved with getDatagramOrigin(). 223 | */ 224 | bool receiveNextPacket(int timeout_ms = -1) { 225 | if (!isOk() || handle == -1) { setErr("not opened.."); return false; } 226 | /* 128k seems to be a reasonable value -- on linux, the max 227 | datagram size appears to be a little bit less than 65536 */ 228 | buffer.resize(1024*128); 229 | 230 | /* check if something is available */ 231 | if (timeout_ms >= 0) { 232 | struct timeval tv; memset(&tv, 0, sizeof tv); 233 | tv.tv_sec=timeout_ms/1000; 234 | tv.tv_usec=(timeout_ms%1000) * 1000; 235 | 236 | //gettimeofday(&tv, 0); //tv.tv_usec += timeout_ms*1000; 237 | 238 | fd_set readset; 239 | FD_ZERO(&readset); 240 | FD_SET(handle, &readset); 241 | //int ret = select( handle+1, &readset, 0, 0, &tv ); 242 | int ret = select( handle+1, &readset, 0, 0, &tv ); 243 | if (ret <= 0) { // error, or timeout 244 | return false; 245 | } 246 | } 247 | 248 | /* now we should be able to read without blocking.. */ 249 | socklen_t len = (socklen_t)remote_addr.maxLen(); 250 | int nread = (int)recvfrom(handle, &buffer[0], (int)buffer.size(), 0, 251 | &remote_addr.addr(), &len); 252 | if (nread < 0) { 253 | // maybe here we should differentiate EAGAIN/EINTR/EWOULDBLOCK from real errors 254 | #ifdef WIN32 255 | if (WSAGetLastError() != WSAEINTR && WSAGetLastError() != WSAEWOULDBLOCK && 256 | WSAGetLastError() != WSAECONNRESET && WSAGetLastError() != WSAECONNREFUSED) { 257 | char s[512]; 258 | #ifdef _MSC_VER 259 | _snprintf_s(s,512,512, "system error #%d", WSAGetLastError()); 260 | #else 261 | snprintf(s,512, "system error #%d", WSAGetLastError()); 262 | #endif 263 | setErr(s); 264 | } 265 | #else 266 | if (errno != EAGAIN && errno != EINTR && errno != EWOULDBLOCK && 267 | errno != ECONNRESET && errno != ECONNREFUSED) { 268 | setErr(strerror(errno)); 269 | } 270 | #endif 271 | if (!isOk()) close(); 272 | return false; 273 | } 274 | if (nread > (int)buffer.size()) { 275 | /* no luck... a large datagram arrived and we truncated it.. now it is too late */ 276 | buffer.clear(); 277 | } else { 278 | buffer.resize(nread); 279 | std::vector tmp(buffer); tmp.swap(buffer); 280 | } 281 | return true; 282 | } 283 | 284 | void *packetData() { return buffer.empty() ? 0 : &buffer[0]; } 285 | size_t packetSize() { return buffer.size(); } 286 | SockAddr &packetOrigin() { return remote_addr; } 287 | 288 | 289 | bool sendPacket(const void *ptr, size_t sz) { 290 | return sendPacketTo(ptr, sz, remote_addr); 291 | } 292 | 293 | bool sendPacketTo(const void *ptr, size_t sz, SockAddr &addr) { 294 | if (!isOk() || handle == -1) { setErr("not opened.."); return false; } 295 | if (!ptr || sz == 0) return false; 296 | 297 | int sent = 0; 298 | do { 299 | int res; 300 | if (isBound()) { 301 | res = sendto(handle, (const char*)ptr, (int)sz, 0, &addr.addr(), (int)addr.actualLen()); 302 | } else { 303 | res = send(handle, (const char*)ptr, (int)sz, 0); 304 | // res = write(handle, ptr, sz); 305 | } 306 | #ifdef WIN32 307 | if (res == -1 && WSAGetLastError() == WSAEINTR) continue; 308 | else sent = res; 309 | #else 310 | //if (res == -1) cerr << "sendto handle=" << handle << ", res:" << res << ", sz=" << sz << ", errno=" << errno << " " << strerror(errno) << "\n"; 311 | if (res == -1 && errno == EINTR) continue; 312 | else sent = res; 313 | #endif 314 | } while (0); 315 | 316 | return (size_t)sent == sz; 317 | } 318 | 319 | private: 320 | bool openSocket(const std::string &hostname, int port, int options) { 321 | char port_string[64]; 322 | #ifdef _MSC_VER 323 | _snprintf_s(port_string, 64, 64, "%d", port); 324 | #else 325 | snprintf(port_string, 64, "%d", port); 326 | #endif 327 | return openSocket(hostname, port_string, options); 328 | } 329 | 330 | bool openSocket(const std::string &hostname, const std::string &port, int options) { 331 | bool binding = hostname.empty(); 332 | close(); error_message.clear(); 333 | 334 | struct addrinfo hints; 335 | struct addrinfo *result = 0, *rp = 0; 336 | 337 | memset(&hints, 0, sizeof(struct addrinfo)); 338 | if (options == OPTION_FORCE_IPV4) hints.ai_family = AF_INET; 339 | else if (options == OPTION_FORCE_IPV6) hints.ai_family = AF_INET6; 340 | else hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 -- in case of problem, try with AF_INET ...*/ 341 | hints.ai_socktype = SOCK_DGRAM; /* Datagram socket */ 342 | hints.ai_flags = (binding ? AI_PASSIVE : 0); /* AI_PASSIVE means socket address is intended for bind */ 343 | 344 | int err = 0; 345 | 346 | 347 | err = getaddrinfo(binding ? 0 : hostname.c_str(), port.empty() ? 0 : port.c_str(), &hints, &result); 348 | if (err != 0) { 349 | setErr(gai_strerror(err)); 350 | return false; 351 | } 352 | 353 | for (rp = result; rp && handle==-1; rp = rp->ai_next) { 354 | 355 | 356 | handle = socket(rp->ai_family, rp->ai_socktype, 357 | rp->ai_protocol); 358 | if (handle == -1) 359 | continue; 360 | 361 | if (binding) { 362 | if (bind(handle, rp->ai_addr, (socklen_t)rp->ai_addrlen) != 0) { 363 | close(); 364 | } else { 365 | socklen_t len = (socklen_t)local_addr.maxLen(); 366 | if (getsockname(handle, &local_addr.addr(), &len) == 0) { 367 | /* great */ 368 | } 369 | break; 370 | } 371 | } else { 372 | if (connect(handle, rp->ai_addr, (socklen_t)rp->ai_addrlen) != 0) { 373 | close(); 374 | } else { 375 | assert((size_t)rp->ai_addrlen <= sizeof remote_addr); 376 | memcpy(&remote_addr.addr(), rp->ai_addr, rp->ai_addrlen); 377 | break; 378 | } 379 | } 380 | } 381 | 382 | 383 | freeaddrinfo(result); result = 0; 384 | 385 | if (!rp) { // we failed miserably 386 | setErr(binding ? "bind failed" : "connect failed"); assert(handle == -1); 387 | return false; 388 | } 389 | return true; 390 | } 391 | }; 392 | 393 | /** dumb struct for parsing OSC urls, such as: osc.udp://foobar:9999/foo/plop/ */ 394 | struct Url { 395 | std::string protocol; 396 | std::string hostname; 397 | std::string port; 398 | std::string path; 399 | int err; 400 | Url() : err(0) {} 401 | Url(const std::string &url) { init(url); } 402 | bool isOk() const { return err == 0; } 403 | bool init(const std::string &url) { 404 | err = 0; 405 | const char *s = url.c_str(); 406 | const char *prot = strstr(s, "osc."); 407 | if (prot == 0) { protocol = "udp"; } 408 | else { 409 | const char *p2 = strstr(prot, "://"); 410 | if (p2) { protocol.assign(prot+4, p2); } 411 | else { err = 1; return false; } 412 | s = p2+3; 413 | } 414 | const char *po = strstr(s, ":"); 415 | if (!po) { err = 2; return false; } 416 | hostname.assign(s, po); 417 | s = po+1; 418 | 419 | const char *pa = strstr(s, "/"); 420 | if (!pa) { port = s; path = "/"; } 421 | else { port.assign(s, pa); path = pa; } 422 | return true; 423 | } 424 | }; 425 | 426 | 427 | } // namespace oscpkt 428 | 429 | #endif // OSCPKT_UDP_HH 430 | -------------------------------------------------------------------------------- /examples/trill-osc/trill-osc.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | ____ _____ _ _ 3 | | __ )| ____| | / \ 4 | | _ \| _| | | / _ \ 5 | | |_) | |___| |___ / ___ \ 6 | |____/|_____|_____/_/ \_\ 7 | http://bela.io 8 | */ 9 | 10 | const char* helpText = 11 | "An OSC client to manage Trill devices." 12 | " Usage: %s [--port ] [[--auto ] ]\n" 13 | " --port : set the port where to listen for OSC messages\n" 14 | "\n" 15 | " `--auto : this is useful for debugging: automatically detect\n" 16 | " all the Trill devices on (corresponding to /dev/i2c-)\n" 17 | " and start reaading from them\n" 18 | " All messages read will be sent to the IP:port (default: %s)\n" 19 | "======================\n" 20 | "\n" 21 | "NOTE: when `--auto` is used, or a `createAll` command is received, the program\n" 22 | "scans several addresses on the i2c bus, which could cause non-Trill\n" 23 | "peripherals connected to it to malfunction.\n" 24 | "\n" 25 | "Command reference:\n" 26 | "Send commands to `/trill/commands/`, with 0 or more arguments.\n" 27 | "\n" 28 | "Global commands:\n" 29 | "list all enabled devices:\n" 30 | " /trill/commands/listAll\n" 31 | "discover and create all Trill devices on the specified `i2cBus`:\n" 32 | " /trill/commands/createAll i2cBus\n" 33 | "delete all active Trill devices:\n" 34 | " /trill/commands/deleteAll\n" 35 | "set whether all devices `should` read (and send) new data automatically or not:\n" 36 | " /trill/commands/autoReadAll should \n" 37 | "change the scanning rate so that there is a `ms` sleep in between reads (and sends):\n" 38 | " /trill/commands/loopSleep, ms\n" 39 | "\n" 40 | "Instance commands: they all start with a string id\n" 41 | "\n" 42 | "create Trill device on the specified I2C `busNumber`, of the specified\n" 43 | "`deviceType`, at the specified `i2cAddress` (optional).\n" 44 | "`deviceType` is a string representing the name of the device (e.g.: bar, square, etc).\n" 45 | "Use `deviceType = unknown` for accepting any type, but then you have to\n" 46 | "specify a valid value for `i2cAddress`.\n" 47 | " /trill/commands/new, id i2cBus deviceType i2cAddress\n" 48 | "delete exising Trill device\n" 49 | " /trill/commands/delete id\n" 50 | "set whether device `should` read (and send) new data automatically (or not)\n" 51 | " /trill/commands/autoRead id should\n" 52 | "ask the device to read (and send) data once\n" 53 | " /trill/commands/readI2C, id\n" 54 | "\n" 55 | "More instance commands, which map directly to the C++ API http://docs.bela.io/classTrill.html\n" 56 | " /trill/commands/updateBaseline id\n" 57 | " /trill/commands/setMode id mode // mode is a string: centroid, raw, baseline, or diff\n" 58 | " /trill/commands/setScanSettings id speed num_bits\n" 59 | " /trill/commands/setPrescaler id value\n" 60 | " /trill/commands/setNoiseThreshold id value\n" 61 | "\n" 62 | "Outbound messages:\n" 63 | "messages in response to commands will be sent to:\n" 64 | " /trill/commandreply\n" 65 | "\n" 66 | "Readings:\n" 67 | "1D devices in centroid mode:\n" 68 | " /trill/readings//touches ...\n" 69 | "2D devices in centroid mode (compoundTouch):\n" 70 | " /trill/readings//touchXY \n" 71 | "Devices in raw/baseline/diff mode:\n" 72 | " /trill/readings//raw ...\n" 73 | " /trill/readings//baseline ...\n" 74 | " /trill/readings//diff ...\n" 75 | ; 76 | 77 | #include 78 | #include 79 | #include 80 | #include 81 | #include 82 | #include 83 | 84 | #define OSCPKT_OSTREAM_OUTPUT 85 | #include "oscpkt.hh" 86 | #include // needed for udp.hh 87 | #include "udp.hh" 88 | 89 | #include 90 | int shouldStop = 0; 91 | bool gAutoReadAll = 0; 92 | unsigned int gLoopSleep = 20; 93 | 94 | void interrupt_handler(int var) 95 | { 96 | shouldStop = true; 97 | } 98 | 99 | typedef enum { 100 | DONT, 101 | ONCE, 102 | ALWAYS, 103 | } ShouldRead; 104 | 105 | struct TrillDev { 106 | std::unique_ptr t; 107 | ShouldRead shouldRead; 108 | }; 109 | 110 | std::map gTouchSensors; 111 | oscpkt::UdpSocket gSock; 112 | 113 | int parseOsc(oscpkt::Message& msg); 114 | int sendOscFloats(const std::string& address, float* values, unsigned int size); 115 | int sendOscTrillDev(const std::string& command, const std::string& id, const TrillDev& trillDev); 116 | int sendOscReply(const std::string& command, const std::string& id, int ret); 117 | std::vector split(const std::string& s, char delimiter); 118 | 119 | int newTrillDev(const std::string& id, unsigned int i2cBus, Trill::Device device, uint8_t i2cAddr, ShouldRead shouldRead) 120 | { 121 | gTouchSensors[id] = {std::unique_ptr(new Trill(i2cBus, device, i2cAddr)), shouldRead}; 122 | Trill& t = *gTouchSensors[id].t; 123 | if(Trill::NONE == t.deviceType()) { 124 | gTouchSensors.erase(id); 125 | return -1; 126 | } 127 | // ensure the sensor scans continuously even though we read it only 128 | // occasionally 129 | t.setAutoScanInterval(1); 130 | printf("Device id: %s\n", id.c_str()); 131 | t.printDetails(); 132 | sendOscTrillDev("new", id, gTouchSensors[id]); 133 | return 0; 134 | } 135 | 136 | void createAllDevicesOnBus(unsigned int i2cBus, bool autoRead) { 137 | printf("Trill devices detected on bus %d\n", i2cBus); 138 | for(uint8_t addr = 0x20; addr <= 0x50; ++addr) 139 | { 140 | Trill::Device device = Trill::probe(i2cBus, addr); 141 | if(Trill::NONE != device) 142 | { 143 | std::string id = std::to_string(i2cBus) + "-" + std::to_string(addr) + "-" + Trill::getNameFromDevice(device); 144 | ShouldRead shouldRead = autoRead ? ALWAYS : DONT; 145 | newTrillDev(id, i2cBus, device, addr, shouldRead); 146 | } 147 | } 148 | } 149 | 150 | static std::string baseAddress = "/trill/"; 151 | int main(int argc, char** argv) 152 | { 153 | int i2cBus = -1; 154 | unsigned int inPort = 7562; 155 | std::string remote = "localhost:7563"; 156 | 157 | int c = 1; 158 | while(c < argc) 159 | { 160 | if(std::string("--help") == std::string(argv[c])) { 161 | printf(helpText, argv[0], remote.c_str()); 162 | return 0; 163 | } 164 | if(std::string("--port") == std::string(argv[c])) { 165 | ++c; 166 | if(c < argc) { 167 | inPort = atoi(argv[c]); 168 | if(!inPort) { 169 | fprintf(stderr, "Invalid port: %s\n", argv[c]); 170 | } 171 | } 172 | } 173 | if(std::string("--auto") == std::string(argv[c])) { 174 | ++c; 175 | if(c < argc) { 176 | i2cBus = atoi(argv[c]); 177 | ++c; 178 | } 179 | if(c < argc) { 180 | remote = argv[c]; 181 | ++c; 182 | } 183 | std::vector spl = split(remote, ':'); 184 | if(2 != spl.size()) { 185 | fprintf(stderr, "Wrong or unparseable `IP:port` argument: %s\n", remote.c_str()); 186 | return 1; 187 | } 188 | gSock.connectTo(spl[0], std::stoi(spl[1])); 189 | std::cout << "Detecting all devices on bus " << i2cBus << ", sending to " << remote << "\n(remote address will be reset when the first inbound message is received)\n"; 190 | gAutoReadAll = true; 191 | createAllDevicesOnBus(i2cBus, gAutoReadAll); 192 | } 193 | ++c; 194 | } 195 | std::cout << "Listening on port " << inPort << "\n"; 196 | 197 | signal(SIGINT, interrupt_handler); 198 | gSock.bindTo(inPort); 199 | 200 | std::string address; 201 | while(!shouldStop) { 202 | for(auto& touchSensor : gTouchSensors) { 203 | if(ShouldRead::DONT == touchSensor.second.shouldRead) 204 | continue; 205 | if(ShouldRead::ONCE == touchSensor.second.shouldRead) 206 | touchSensor.second.shouldRead = ShouldRead::DONT; 207 | Trill& t = *(touchSensor.second.t); 208 | t.readI2C(); 209 | address = baseAddress + "readings/" + touchSensor.first; 210 | if(Trill::CENTROID == t.getMode()) { 211 | float values[11]; 212 | unsigned int len = 0; 213 | unsigned int numTouches = 0; 214 | if(t.is2D()) { 215 | address += "/touchXY"; 216 | float size = t.compoundTouchSize(); 217 | float touchY = t.compoundTouchLocation(); 218 | float touchX = t.compoundTouchHorizontalLocation(); 219 | values[1] = touchX; 220 | values[2] = touchY; 221 | values[3] = size; 222 | numTouches = size > 0; 223 | len = 1 + numTouches * 3; 224 | } else { 225 | address += "/touches"; 226 | numTouches = t.getNumTouches(); 227 | for(unsigned int i = 0; i < numTouches; i++) { 228 | values[1 + i * 2] = t.touchLocation(i); 229 | values[1 + i * 2 + 1] = t.touchSize(i); 230 | } 231 | len = 1 + numTouches * 2; 232 | } 233 | values[0] = numTouches; 234 | sendOscFloats(address, values, len); 235 | } else { 236 | address += "/diff"; 237 | sendOscFloats(address, t.rawData.data(), t.rawData.size()); 238 | } 239 | } 240 | // process incoming mesages 241 | while(!shouldStop && gSock.isOk() && gSock.receiveNextPacket(1)) {// timeout 242 | static bool connected = false; 243 | if(!connected) { 244 | std::vector origin = split(gSock.packetOrigin().asString(), ':'); 245 | if(origin.size() != 2) { 246 | fprintf(stderr, "Something wrong with the address we received from\n"); 247 | continue; 248 | } 249 | std::cout << "Connecting to " << origin[0] << ":" << origin[1] << "\n"; 250 | gSock.connectTo(origin[0], origin[1]); 251 | gSock.bindTo(inPort); 252 | connected = true; 253 | } 254 | 255 | oscpkt::PacketReader pr(gSock.packetData(), gSock.packetSize()); 256 | oscpkt::Message *msg; 257 | while (!shouldStop && pr.isOk() && (msg = pr.popMessage()) != 0) { 258 | std::cout << "Received " << *msg << "\n"; 259 | parseOsc(*msg); 260 | } 261 | 262 | } 263 | int sleep = gLoopSleep; 264 | // sleep overall about gLoopSleep ms, but if it's too large, do it 265 | // in smaller chunks to be reactive to incoming messages 266 | int chunk = 10; // chunks of 10ms 267 | int slept = 0; 268 | while(!shouldStop && sleep > 0) { 269 | if(sleep >= chunk) { 270 | sleep -= chunk; 271 | slept += chunk; 272 | usleep(chunk * 1000); 273 | } else { 274 | slept += sleep; 275 | usleep(sleep * 1000); 276 | sleep = 0; 277 | } 278 | } 279 | } 280 | return 0; 281 | } 282 | 283 | // 1.3 of https://www.fluentcpp.com/2017/04/21/how-to-split-a-string-in-c/ 284 | #include 285 | std::vector split(const std::string& s, char delimiter) 286 | { 287 | std::vector tokens; 288 | std::string token; 289 | std::istringstream tokenStream(s); 290 | while (std::getline(tokenStream, token, delimiter)) 291 | { 292 | tokens.push_back(token); 293 | } 294 | return tokens; 295 | } 296 | 297 | int sendOscList(); 298 | 299 | int parseOsc(oscpkt::Message& msg) 300 | { 301 | oscpkt::Message::ArgReader args = msg.partialMatch(baseAddress + "command/"); 302 | if(!args.isOk()) { 303 | return -1; 304 | } 305 | std::string typeTags = msg.typeTags(); 306 | std::cout << "Trill: received " << msg << "\n"; 307 | std::vector tokens = split(msg.addressPattern(), '/'); 308 | if(tokens.size() != 4) { 309 | fprintf(stderr, "Unexpected address pattern %s\n", msg.addressPattern().c_str()); 310 | return -1; 311 | } 312 | const std::string command = tokens.back(); 313 | printf("Command: %s\n", command.c_str()); 314 | 315 | // tmp variables for args parsing 316 | float value0; 317 | float value1; 318 | std::string str0; 319 | 320 | // global commands 321 | if("listAll" == command && args.isOkNoMoreArgs()) { 322 | printf("listAll\n"); 323 | sendOscList(); 324 | return 0; 325 | } else if("createAll" == command && "f" == typeTags && args.popFloat(value0).isOkNoMoreArgs()) 326 | { 327 | printf("createAll %f\n", value0); 328 | createAllDevicesOnBus(value0, gAutoReadAll); 329 | return 0; 330 | } else if ("deleteAll" == command && args.isOkNoMoreArgs()) { 331 | printf("deleteAll\n"); 332 | gTouchSensors.clear(); 333 | return 0; 334 | } else if ("autoReadAll" == command && "f" == typeTags && args.popFloat(value0).isOkNoMoreArgs()) { 335 | printf("autoReadAll %f\n", value0); 336 | gAutoReadAll = value0; 337 | for(auto& t : gTouchSensors) 338 | t.second.shouldRead = gAutoReadAll ? ALWAYS : DONT; 339 | return 0; 340 | } else if ("loopSleep" == command && "f" == typeTags && args.popFloat(value0).isOkNoMoreArgs()) { 341 | printf("loopSleep %f\n", value0); 342 | gLoopSleep = value0; 343 | return 0; 344 | } 345 | 346 | //instance commands: they all start with an id 347 | // check and retrieve first argument: id 348 | if(typeTags[0] != 's') { 349 | std::cerr << "Unknown message or wrong argument list " << msg << "\n"; 350 | return -1; 351 | } 352 | std::string id; 353 | args.popStr(id); 354 | typeTags = typeTags.substr(1);// peel it off 355 | 356 | // only "new" can use a non-existing id 357 | if(gTouchSensors.find(id) == gTouchSensors.end() && "new" != command) { 358 | fprintf(stderr, "Unknown id: %s at %s\n", id.c_str(), msg.addressPattern().c_str()); 359 | return -1; 360 | } 361 | if("new" == command) { 362 | bool ok = false; 363 | std::string deviceName; 364 | float bus; 365 | const float defaultI2cAddr = 255; 366 | float i2cAddr = defaultI2cAddr; 367 | // at least two arguments: bus, deviceName 368 | if(typeTags.size() >= 2 && 'f' == typeTags[0] && 's' == typeTags[1]) { 369 | args.popFloat(bus).popStr(deviceName); 370 | if( 371 | // optional third argument: address 372 | (typeTags.size() == 3 && 'f' == typeTags[2] && args.popFloat(i2cAddr).isOkNoMoreArgs()) 373 | || args.isOkNoMoreArgs() 374 | ) 375 | { 376 | Trill::Device device = Trill::getDeviceFromName(deviceName); 377 | if(Trill::UNKNOWN != device || defaultI2cAddr != i2cAddr) 378 | ok = true; 379 | } 380 | } 381 | if(ok) { 382 | printf("new %s %f %s %f\n", id.c_str(), bus, deviceName.c_str(), i2cAddr); 383 | newTrillDev(id, bus, Trill::getDeviceFromName(deviceName), i2cAddr, gAutoReadAll ? ALWAYS : DONT); 384 | return 0; 385 | } else { 386 | std::cerr << "Unknown message or wrong argument list " << msg << "\n"; 387 | return 1; 388 | } 389 | } else if ("delete" == command) { 390 | gTouchSensors.erase(id); 391 | printf("delete\n"); 392 | return 0; 393 | } 394 | // commands below need to access a device. If we get to this line, the 395 | // device exists in gTouchSensors 396 | Trill& t = *(gTouchSensors[id].t); 397 | int ret; 398 | printf("id: %s - ", id.c_str()); 399 | if ("autoRead" == command && "f" == typeTags && args.popFloat(value0).isOkNoMoreArgs()) { 400 | printf("autoRead: %f\n", value0); 401 | gTouchSensors[id].shouldRead = value0 ? ALWAYS : DONT; 402 | } else if ("readI2C" == command && args.isOkNoMoreArgs()) { 403 | printf("readI2C\n"); 404 | gTouchSensors[id].shouldRead = ONCE; 405 | } // commands below simply map to the corresponding methods of the Trill class 406 | else if("updateBaseline" == command && args.isOkNoMoreArgs()) { 407 | printf("updateBaseline\n"); 408 | ret = t.updateBaseline(); 409 | sendOscReply(command, id, ret); 410 | } else if ("setScanSettings" == command && "ff" == typeTags && args.popFloat(value0).popFloat(value1).isOkNoMoreArgs()) { 411 | printf("setScanSettings %f %f\n", value0, value1); 412 | ret = t.setScanSettings(value0, value1); 413 | sendOscReply(command, id, ret); 414 | } else if ("setPrescaler" == command && "f" == typeTags && args.popFloat(value0).isOkNoMoreArgs()) { 415 | printf("setPrescaler %f\n", value0); 416 | ret = t.setPrescaler(value0); 417 | sendOscReply(command, id, ret); 418 | } else if ("setNoiseThreshold" == command && "f" == typeTags && args.popFloat(value0).isOkNoMoreArgs()) { 419 | printf("setNoiseThreshold %f\n", value0); 420 | ret = t.setNoiseThreshold(value0); 421 | sendOscReply(command, id, ret); 422 | } else if ("setMode" == command && "s" == typeTags && args.popStr(str0).isOkNoMoreArgs()) { 423 | printf("setMode: %s\n", str0.c_str()); 424 | ret = t.setMode(Trill::getModeFromName(str0)); 425 | sendOscReply(command, id, ret); 426 | } else { 427 | std::cerr << "Unknown message or wrong argument list " << msg << "\n"; 428 | return 1; 429 | } 430 | return 0; 431 | } 432 | 433 | int sendOsc(const oscpkt::Message& msg) 434 | { 435 | oscpkt::PacketWriter pw; 436 | pw.addMessage(msg); 437 | bool ok = gSock.sendPacket(pw.packetData(), pw.packetSize()); 438 | if(!ok) { 439 | fprintf(stderr, "could not send\n"); 440 | return -1; 441 | } 442 | return 0; 443 | } 444 | 445 | int sendOscFloats(const std::string& address, float* values, unsigned int size) 446 | { 447 | if(!size) 448 | return 0; 449 | oscpkt::Message msg(address); 450 | for(unsigned int n = 0; n < size; ++n) 451 | msg.pushFloat(values[n]); 452 | return sendOsc(msg); 453 | } 454 | 455 | std::string commandReplyAddress = baseAddress + "commandreply/"; 456 | int sendOscList() 457 | { 458 | for(auto& d : gTouchSensors) 459 | sendOscTrillDev("list", d.first, d.second); 460 | return 0; 461 | } 462 | 463 | int sendOscTrillDev(const std::string& command, const std::string& id, const TrillDev& trillDev) 464 | { 465 | oscpkt::Message msg(commandReplyAddress + "/" + command); 466 | msg.pushStr(id); 467 | Trill& t = *trillDev.t; 468 | msg.pushStr(Trill::getNameFromDevice(t.deviceType())); 469 | msg.pushFloat(t.getAddress()); 470 | msg.pushStr(Trill::getNameFromMode(t.getMode())); 471 | return sendOsc(msg); 472 | } 473 | 474 | int sendOscReply(const std::string& command, const std::string& id, int ret) 475 | { 476 | oscpkt::Message msg(commandReplyAddress + command); 477 | msg.pushStr(id); 478 | msg.pushFloat(ret); 479 | return sendOsc(msg); 480 | } 481 | -------------------------------------------------------------------------------- /lib/Trill.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | /** 8 | * \brief A class to use the Trill family of capacitive sensors. 9 | * http://bela.io/trill 10 | * \nosubgrouping 11 | */ 12 | 13 | class Trill : public I2c 14 | { 15 | public: 16 | /** 17 | * The acquisition modes that a device can be set to. 18 | */ 19 | typedef enum { 20 | AUTO = -1, /**< Auto mode: the mode is set 21 | automatically based on the device type */ 22 | CENTROID = 0, /**< Centroid mode: detect discrete touches */ 23 | RAW = 1, /**< Raw mode */ 24 | BASELINE = 2, /**< Baseline mode */ 25 | DIFF = 3, /**< Differential mode */ 26 | } Mode; 27 | 28 | /** 29 | * The types of Trill devices 30 | */ 31 | typedef enum { 32 | NONE = -1, ///< No device 33 | ANY = 0, ///< A valid device of unknown type 34 | BAR = 1, ///< %Trill Bar 35 | SQUARE = 2, ///< %Trill Square 36 | CRAFT = 3, ///< %Trill Craft 37 | RING = 4, ///< %Trill Ring 38 | HEX = 5, ///< %Trill Hex 39 | FLEX = 6, ///< %Trill Flex 40 | UNKNOWN = ANY, ///< same as ANY, for backwards compatibility 41 | } Device; 42 | /** 43 | * Controls when the EVT pin will be set when a new frame is 44 | * available. In this context, the meaning "activity is detected" 45 | * depends on the #Mode in which the device is: 46 | * - in #CENTROID mode, activity is detected if one or more 47 | * touches are detected 48 | * - in any other mode, activity is detected if any channel 49 | * after formatting is non-zero. 50 | */ 51 | typedef enum { 52 | kEventModeTouch = 0, ///< Only set the EVT pin if activity is detected in the current frame 53 | kEventModeChange = 1, ///< Only set the EVT pin if activity is detected in the current or past frame 54 | kEventModeAlways = 2, ///< Set the EVT pin every time a new frame is available 55 | } EventMode; 56 | /** 57 | * 58 | */ 59 | typedef enum { 60 | kScanTriggerDisabled = 0x0, ///< Do not scan capacitive channels. 61 | kScanTriggerI2c = 0x1, ///< Scan capacitive channels after every I2C transaction 62 | kScanTriggerTimer = 0x2, ///< Scan capacitive channels every time the timer set by setAutoScanInterval() expires 63 | kScanTriggerI2cOrTimer = 0x3, ///< Scan capacitive channels after every I2C transaction or when timer expires, whichever comes first. 64 | } ScanTriggerMode; 65 | private: 66 | Mode mode_; // Which mode the device is in 67 | Device device_type_ = NONE; // Which type of device is connected (if any) 68 | uint32_t frameId; 69 | uint8_t statusByte; 70 | uint8_t address; 71 | uint8_t firmware_version_ = 0; // Firmware version running on the device 72 | uint8_t num_touches_; // Number of touches on last read 73 | bool dataBufferIncludesStatusByte = false; 74 | bool quiet = false; 75 | std::vector dataBuffer; 76 | uint16_t commandSleepTime = 1000; 77 | size_t currentReadOffset = -1; 78 | bool shouldReadFrameId = false; 79 | unsigned int numBits; 80 | unsigned int transmissionWidth = 16; 81 | unsigned int transmissionRightShift = 0; 82 | uint32_t channelMask; 83 | uint8_t numChannels; 84 | float posRescale; 85 | float posHRescale; 86 | float sizeRescale; 87 | float rawRescale; 88 | ScanTriggerMode scanTriggerMode; 89 | int identify(); 90 | void updateRescale(); 91 | void parseNewData(bool includesStatusByte); 92 | void processStatusByte(uint8_t newStatusByte); 93 | int writeCommandAndHandle(const i2c_char_t* data, size_t size, const char* name); 94 | int writeCommandAndHandle(i2c_char_t command, const char* name); 95 | int readBytesFrom(uint8_t offset, i2c_char_t* data, size_t size, const char* name); 96 | int readBytesFrom(uint8_t offset, i2c_char_t& byte, const char* name); 97 | int waitForAck(uint8_t command, const char* name); 98 | void updateChannelMask(uint32_t mask); 99 | int verbose = 0; 100 | uint8_t cmdCounter = 0; 101 | bool readErrorOccurred; 102 | bool enableVersionCheck = true; 103 | public: 104 | /** 105 | * @name RAW, BASELINE or DIFF mode 106 | * When the device is in #RAW, #BASELINE, or #DIFF mode, the 107 | * readings from the individual sensing channels are accessed 108 | * through #rawData. 109 | * @{ 110 | * @class TAGS_canonical_return 111 | * @return 0 on success or an error code otherwise. 112 | * 113 | * @class TAGS_firmware_3_error 114 | * \note This feature is only available with devices starting 115 | * from firmware version 3. On older devices calling this 116 | * function has no effect and it will return an error. 117 | * 118 | * @class TAGS_firmware_3_undef 119 | * \note This feature is only available with devices starting from firmware 120 | * version 3. On older devices calling this function has no effect and its 121 | * return value is undefined. 122 | */ 123 | /** 124 | * An array containing the readings from the device's 125 | * channel when the device is in 126 | * #RAW, #BASELINE or #DIFF mode. 127 | * 128 | * The type of data it contains depend on the device mode: 129 | * - #RAW: the #rawData array contains 130 | * the raw readings of each individual capacitive sensing 131 | * channel. This corresponds to `CSD_waSnsResult`. 132 | * - #BASELINE:the #rawData 133 | * array contains the baseline readings of each individual 134 | * capacitive sensing channel. 135 | * This corresponds to `CSD_waSnsBaseline`. 136 | * - #DIFF: the #rawData array 137 | * contains differential readings between the baseline and 138 | * the raw reading. This corresponds to `CSD_waSnsDiff`. 139 | */ 140 | std::vector rawData; 141 | /** @} */ 142 | 143 | /** 144 | * An array containing the valid values for the speed parameter 145 | * in setScanSettings() 146 | */ 147 | static constexpr uint8_t speedValues[4] = {0, 1, 2, 3}; 148 | /** 149 | * The maximum value for the setPrescaler() method 150 | */ 151 | static constexpr uint8_t prescalerMax = 8; 152 | Trill(); 153 | ~Trill(); 154 | /** 155 | * Initialise the device. 156 | * 157 | * @param i2c_bus the bus that the device is connected to. 158 | * @param device the device type. 159 | * 160 | * @param i2c_address the address at which the device can be 161 | * found. 162 | * 163 | * If @p device is #ANY then: 164 | * - if \p i2c_address is a valid address, then 165 | * any device detected at that addres will be accepted 166 | * - if \p i2c_address is `255` or unspecified, then the range of 167 | * valid addresses will be scanned, stopping at the first 168 | * valid device encountered. Use this with caution as it may 169 | * affect the behaviour of non-Trill devices on the I2C bus. 170 | * 171 | * Otherwise, if @p i2c_address is `255` or unspecified, 172 | * the default address for the specified device type will be used. 173 | */ 174 | Trill(unsigned int i2c_bus, Device device, uint8_t i2c_address = 255); 175 | /** 176 | * \copydoc Trill::Trill(unsigned int, Device, uint8_t) 177 | * 178 | * \copydoc TAGS_canonical_return 179 | */ 180 | int setup(unsigned int i2c_bus, Device device = ANY, uint8_t i2c_address = 255); 181 | 182 | /** 183 | * Probe the bus for a device at the specified address. 184 | * 185 | * @return The type of the device that was found. If no device 186 | * was found, #NONE is returned. 187 | */ 188 | static Device probe(unsigned int i2c_bus, uint8_t i2c_address); 189 | 190 | /** 191 | * Probe the bus for a device at any valid address. 192 | * \warning Use with caution as it may affect the behaviour of 193 | * non-Trill devices on the I2C bus. 194 | * 195 | * @param i2c_bus the I2C bus to scan 196 | * @param maxCount stop discovering new devices after this many 197 | * have been discovered. Use 0 to find all possible devices. 198 | * 199 | * @return A vector containing the #Device and address pairs 200 | * identified. 201 | */ 202 | static std::vector > probeRange(unsigned int i2c_bus, size_t maxCount = 0); 203 | 204 | /** 205 | * Update the baseline value on the device. 206 | */ 207 | int updateBaseline(); 208 | /** 209 | * Reset the chip. 210 | */ 211 | int reset(); 212 | 213 | /** 214 | * \brief Read data from the device. 215 | * 216 | * Performs an I2C transaction with the device to retrieve new data 217 | * and parse them. Users calling this method won't need to call newData(). 218 | * 219 | * @param shouldReadStatusByte whether or not to read the 220 | * status byte as part of the transaction. If the firmware 221 | * version is lower than 3, this should be set to `false`. 222 | * 223 | * \copydoc TAGS_canonical_return 224 | */ 225 | int readI2C(bool shouldReadStatusByte = false); 226 | 227 | /** 228 | * \brief Set data retrieved from the device. 229 | * 230 | * Sets the data retrieved from the device. 231 | * This can be used to pass to the object 232 | * data retrieved elsewhere (e.g.: from an I2C DMA callback). 233 | * Users calling readI2C() won't need to call this method. 234 | * 235 | * @param newData A pointer to an array containing new data. 236 | * @param len The length of the array. For proper operation, this 237 | * should be the value returned from getBytesToRead(). 238 | * @param includesStatusByte whether #newData includes the 239 | * status byte or not. 240 | */ 241 | void newData(const uint8_t* newData, size_t len, bool includesStatusByte = false); 242 | 243 | /** 244 | * Get the device type. 245 | */ 246 | Device deviceType() { return device_type_; } 247 | /** 248 | * Get the name from the device. 249 | */ 250 | static const std::string& getNameFromDevice(Device device); 251 | /** 252 | * Get the device from the name. 253 | */ 254 | static Device getDeviceFromName(const std::string& name); 255 | /** 256 | * Get the mode from the name. 257 | */ 258 | static const std::string& getNameFromMode(Mode mode); 259 | /** 260 | * Get the mode from the name. 261 | */ 262 | static Mode getModeFromName(const std::string& name); 263 | /** 264 | * Get the firmware version of the device. 265 | */ 266 | int firmwareVersion() { return firmware_version_; } 267 | /** 268 | * Get the mode that the device is currently in. 269 | */ 270 | Mode getMode() { return mode_; } 271 | /** 272 | * Get the current address of the device. 273 | */ 274 | uint8_t getAddress() { return address; } 275 | /** 276 | * Print details about the device to standard output 277 | */ 278 | void printDetails() ; 279 | /** 280 | * Print more details about I/O transactions as they happen. 281 | */ 282 | void setVerbose(int verbose); 283 | /** 284 | * Get the number of capacitive channels currently active on the device. 285 | */ 286 | unsigned int getNumChannels() const; 287 | /** 288 | * Get the number of capacitive channels available on the device. 289 | */ 290 | unsigned int getDefaultNumChannels() const; 291 | 292 | /** 293 | * @name Scan Configuration Settings 294 | * @{ 295 | * 296 | * Some of the methods below map directly to function calls and 297 | * variables with the `CSD_` prefix, which are described in the 298 | * [Cypress CapSense Sigma-Delta * Datasheet 299 | * v2.20](https://www.cypress.com/file/124551/download). 300 | */ 301 | /** 302 | * Set the operational mode of the device. 303 | * 304 | * @param mode The device mode. The special mode #AUTO, selects the 305 | * device-specific default mode for the _detected_ device type. 306 | * \copydoc TAGS_canonical_return 307 | */ 308 | int setMode(Mode mode); 309 | /** 310 | * Set the speed and bit depth of the capacitive scanning. 311 | * This triggers a call to `CSD_SetScanMode(speed, num_bits)` 312 | * on the device. 313 | * 314 | * @param speed The speed of the scanning 315 | * Valid values of speed are, ordered by decreasing speed, are 316 | * comprised between 0 (`CSD_ULTRA_FAST_SPEED`) and 3 (`CSD_SLOW_SPEED`) 317 | * @param num_bits The bit depth of the scanning. 318 | * Valid values are comprised between 9 and 16. 319 | * \copydoc TAGS_canonical_return 320 | */ 321 | int setScanSettings(uint8_t speed, uint8_t num_bits = 12); 322 | /** 323 | * Set the prescaler value for the capacitive scanning. 324 | * This triggers a call to `CSD_SetPrescaler(prescaler)` 325 | * on the device. 326 | * 327 | * @param prescaler The prescaler value. Valid values are 328 | * between 0 and 8, inclusive, and map directly to values 329 | * `CSD_PRESCALER_1` to `CSD_PRESCALER_256`. 330 | * \copydoc TAGS_canonical_return 331 | */ 332 | int setPrescaler(uint8_t prescaler); 333 | /** 334 | * Set the noise threshold for the capacitive channels. 335 | * 336 | * When a channel's scan returns a value smaller than the 337 | * threshold, its value is set to 0. 338 | * 339 | * @param threshold the noise threshold level. Valid values are 340 | * between 0 and `255.0/(1 << numBits)`. 341 | * The value is internally converted to an 8-bit integer by 342 | * multiplying it times `1 << numBits` before being sent to the device. 343 | * On the device, the received value is used to set the 344 | * `CSD_bNoiseThreshold` variable. 345 | * @return 0 on success, or an error code otherwise. 346 | */ 347 | int setNoiseThreshold(float threshold); 348 | /** 349 | * Sets the IDAC value for the device. 350 | * 351 | * This triggers a call to `CSD_SetIdacValue(value)` on the device. 352 | * 353 | * @param value the IDAC value. Valid values are between 0 and 255. 354 | * \copydoc TAGS_canonical_return 355 | */ 356 | int setIDACValue(uint8_t value); 357 | /** 358 | * Set minimum touch size 359 | * 360 | * Sets the minimum touch size below which a touch is ignored. 361 | * \copydoc TAGS_canonical_return 362 | * 363 | */ 364 | int setMinimumTouchSize(float minSize); 365 | /** 366 | * Set how the device triggers a new scan of its capacitive 367 | * channels. 368 | * 369 | * @param arg One of the #ScanTriggerMode values 370 | * 371 | * \copydoc TAGS_firmware_3_error 372 | * \copydoc TAGS_canonical_return 373 | */ 374 | int setScanTrigger(ScanTriggerMode scanTriggerMode); 375 | /** 376 | * Set the interval for scanning capacitive channels when the 377 | * device's scanning is triggered by the timer. 378 | * 379 | * @param ms the scanning period, measured in milliseconds. The 380 | * effective minimum scanning period will be limited by the scanning 381 | * speed, bit depth and any computation happening on the device 382 | * (such as touch detection). Granularity is 1 ms for values 383 | * until 255 ms and higher after that. Maximum value is just 384 | * above 2032 ms. 385 | * Scanning on timer has to be separately enabled via setScanTrigger(). 386 | * When @p ms is not greater than zero, the timer is disabled. 387 | * 388 | * \note The 32kHz clock often deviates by 10% or more from its 389 | * nominal frequency, thus affecting the accuracy of the timer. 390 | */ 391 | int setTimerPeriod(float ms); 392 | /** 393 | * Deprecated. Same as setTimerPeriod(), but the @p interval is 394 | * expressed as cycles of a 32kHz clock. On devices with 395 | * firmware 2, @p interval is used directly. On devices with 396 | * firwmare 3 or above, it is quantised to blocks of at least 397 | * 1 ms. 398 | */ 399 | int setAutoScanInterval(uint16_t interval); 400 | /** 401 | * Set how the EVT pin behaves. 402 | * 403 | * @param mode an #EventMode denoting the required behaviour. 404 | * 405 | * \copydoc TAGS_canonical_return 406 | * \copydoc TAGS_firmware_3_error 407 | */ 408 | int setEventMode(EventMode mode); 409 | /** 410 | * Set a channel mask identifying which scanning channels are 411 | * enabled. 412 | * 413 | * @param mask The channel mask. Bits 0 to 31 identify channels 414 | * 0 to 31 respectively. Bit positions higher than the value 415 | * returned by getDefaultNumChannels() are ignored. 416 | * 417 | * \copydoc TAGS_canonical_return 418 | * \copydoc TAGS_firmware_3_error 419 | */ 420 | int setChannelMask(uint32_t mask); 421 | /** 422 | * Set the format used for transmission of non-centroid data 423 | * from the device to the host. 424 | * 425 | * @param width The data width. If a value would overflow when 426 | * stored, it is clipped. 427 | * @param shift Number of right shift operations applied on the 428 | * value before being stored in the word. 429 | * 430 | * \copydoc TAGS_canonical_return 431 | * \copydoc TAGS_firmware_3_error 432 | */ 433 | int setTransmissionFormat(uint8_t width, uint8_t shift); 434 | /** @} */ // end of Scan Configuration Settings 435 | /** 436 | * @name Status byte 437 | * @{ 438 | */ 439 | /** 440 | * Read the status byte from the device. 441 | * Alternatively, the status byte can be read as part of 442 | * reading data by calling readI2C(true). 443 | * 444 | * @return the status byte, or a negative value in case of 445 | * error. As a successful call also updates the 446 | * internal state, the caller is probably better off calling 447 | * getFrameId(), hasActivity(), hasReset() instead of parsing 448 | * the status byte directly. 449 | * 450 | * \copydoc TAGS_firmware_3_undef 451 | */ 452 | int readStatusByte(); 453 | /** 454 | * Whether the device has reset since a identify command was 455 | * last written to it. 456 | * 457 | * This relies on a current status byte. 458 | * 459 | * \copydoc TAGS_firmware_3_error 460 | */ 461 | bool hasReset(); 462 | /** 463 | * Whether activity has been detected in the current frame. 464 | * 465 | * This relies on a current status byte. 466 | * 467 | * \copydoc TAGS_firmware_3_undef 468 | */ 469 | bool hasActivity(); 470 | /** 471 | * Get the frameId. 472 | * This relies on a current status byte. 473 | * 474 | * \copydoc TAGS_firmware_3_undef 475 | */ 476 | uint8_t getFrameId(); 477 | /** 478 | * Same as above, but it tries to unwrap the 6-bit frameId into 479 | * a uint32_t counter. 480 | * This relies on reading several status bytes over time. 481 | * The counter is guaranteed monotonic, but it can only be 482 | * regarded as an actual frame counter if the status byte is 483 | * read at least once every 63 frames. 484 | * 485 | * @return the counter 486 | * \copydoc TAGS_firmware_3_undef 487 | */ 488 | uint32_t getFrameIdUnwrapped(); 489 | /** 490 | * @} 491 | */ 492 | 493 | /** 494 | * @name Centroid Mode 495 | * @{ 496 | * 497 | * When the device is in #CENTROID mode, touches are 498 | * detected as discrete entities and can be retrieved with 499 | * the methods in this section. 500 | * 501 | * The `location` of a touch is a normalised value where `0` and 502 | * `1` are the extremes of the axis. 503 | * 504 | * The `size` of a touch is a rescaled value of the total 505 | * activation measured on the sensing channels that contribute 506 | * to the touch. The amount of activation for a touch of a 507 | * given size is dependent (among other things) on the geometry 508 | * of the device. The values used here have been determined 509 | * empirically. 510 | * 511 | * A `compoundTouch` is a single touch represntation obtained 512 | * by averaging the location and size of the touches on each 513 | * axis and their size. 514 | * This is most useful for 2-axes devices, in order to get a 515 | * single touch. 516 | * 517 | * @class TAGS_1d 518 | * \note It is only valid to call this method if one of is1D() and 519 | * is2D() returns `true`. 520 | * @class TAGS_2d 521 | * \note It is only valid to call this method is2D() returns `true` 522 | */ 523 | /** 524 | * Does the device have one axis of position sensing? 525 | * 526 | * @return `true` if the device has one axis of position sensing 527 | * and is set in #CENTROID mode, `false` 528 | * otherwise. 529 | */ 530 | bool is1D(); 531 | /** 532 | * Does the device have two axes of position sensing? 533 | * 534 | * @return `true` if the device has two axes of position sensing 535 | * and is set in #CENTROID mode, `false` 536 | * otherwise. 537 | */ 538 | bool is2D(); 539 | /** 540 | * Return the number of bytes to read when reading data. 541 | */ 542 | unsigned int getBytesToRead(bool includesStatusByte); 543 | /** 544 | * Return the number of "button" channels on the device. 545 | */ 546 | unsigned int getNumButtons() { return 2 * (getMode() == CENTROID && RING == deviceType());}; 547 | /** 548 | * Get the number of touches currently active on the 549 | * vertical axis of the device. 550 | * 551 | * \copydoc TAGS_1d 552 | */ 553 | unsigned int getNumTouches(); 554 | /** 555 | * Get the location of a touch on the vertical axis of the 556 | * device. 557 | * 558 | * \copydoc TAGS_1d 559 | * 560 | * @param touch_num the number of the touch. This value needs 561 | * to be comprised between 0 and `getNumTouches() - 1`. 562 | * @return the position of the touch relative to the axis, or 563 | * -1 if no such touch exists. 564 | */ 565 | float touchLocation(uint8_t touch_num); 566 | /** 567 | * Get the size of a touch. 568 | * 569 | * \copydoc TAGS_1d 570 | * 571 | * @return the size of the touch, if the touch exists, or 0 572 | * otherwise. 573 | */ 574 | float touchSize(uint8_t touch_num); 575 | /** 576 | * Get the number of touches currently active on the 577 | * horizontal axis of the device. 578 | * 579 | * \copydoc TAGS_2d 580 | */ 581 | unsigned int getNumHorizontalTouches(); 582 | /** 583 | * Get the location of a touch on the horizontal axis of the 584 | * device. 585 | * 586 | * \copydoc TAGS_2d 587 | * 588 | * @param touch_num the number of the touch. This value needs 589 | * to be comprised between 0 and `getNumHorizontalTouches() - 1`. 590 | * @return the position of the touch relative to the axis, or 591 | * -1 if no such touch exists. 592 | * */ 593 | float touchHorizontalLocation(uint8_t touch_num); 594 | /** 595 | * Get the size of a touch. 596 | * 597 | * \copydoc TAGS_2d 598 | * 599 | * @return the size of the touch, if the touch exists, or 0 600 | * otherwise. 601 | */ 602 | float touchHorizontalSize(uint8_t touch_num); 603 | /** 604 | * Get the vertical location of the compound touch on the 605 | * device. 606 | * 607 | * \copydoc TAGS_1d 608 | * */ 609 | float compoundTouchLocation(); 610 | /** 611 | * Get the horizontal location of the compound touch on the 612 | * device. 613 | * 614 | * \copydoc TAGS_1d 615 | */ 616 | float compoundTouchHorizontalLocation(); 617 | /** 618 | * Get the size of the compound touch on the 619 | * device. 620 | * 621 | * \copydoc TAGS_1d 622 | */ 623 | float compoundTouchSize(); 624 | /** 625 | * Get the value of the capacitive "button" channels on the 626 | * device 627 | * 628 | * @param button_num the button number. Valid values are 629 | * comprised between `0` and `getNumButtons() - 1`. 630 | * @return The differential reading on the button, normalised 631 | * between 0 and 1. 632 | */ 633 | float getButtonValue(uint8_t button_num); 634 | 635 | /** @}*/ // end of centroid mode 636 | }; 637 | -------------------------------------------------------------------------------- /examples/trill-osc/oscpkt.hh: -------------------------------------------------------------------------------- 1 | /** @mainpage OSCPKT : a minimalistic OSC ( http://opensoundcontrol.org ) c++ library 2 | 3 | Before using this file please take the time to read the OSC spec, it 4 | is short and not complicated: http://opensoundcontrol.org/spec-1_0 5 | 6 | Features: 7 | - handles basic OSC types: TFihfdsb 8 | - handles bundles 9 | - handles OSC pattern-matching rules (wildcards etc in message paths) 10 | - portable on win / macos / linux 11 | - robust wrt malformed packets 12 | - optional udp transport for packets 13 | - concise, all in a single .h file 14 | - does not throw exceptions 15 | 16 | does not: 17 | - take into account timestamp values. 18 | - provide a cpu-scalable message dispatching. 19 | - not suitable for use inside a realtime thread as it allocates memory when 20 | building or reading messages. 21 | 22 | 23 | There are basically 3 classes of interest: 24 | - oscpkt::Message : read/write the content of an OSC message 25 | - oscpkt::PacketReader : read the bundles/messages embedded in an OSC packet 26 | - oscpkt::PacketWriter : write bundles/messages into an OSC packet 27 | 28 | And optionaly: 29 | - oscpkt::UdpSocket : read/write OSC packets over UDP. 30 | 31 | @example: oscpkt_demo.cc 32 | @example: oscpkt_test.cc 33 | */ 34 | 35 | /* Copyright (C) 2010 Julien Pommier 36 | 37 | This software is provided 'as-is', without any express or implied 38 | warranty. In no event will the authors be held liable for any damages 39 | arising from the use of this software. 40 | 41 | Permission is granted to anyone to use this software for any purpose, 42 | including commercial applications, and to alter it and redistribute it 43 | freely, subject to the following restrictions: 44 | 45 | 1. The origin of this software must not be misrepresented; you must not 46 | claim that you wrote the original software. If you use this software 47 | in a product, an acknowledgment in the product documentation would be 48 | appreciated but is not required. 49 | 2. Altered source versions must be plainly marked as such, and must not be 50 | misrepresented as being the original software. 51 | 3. This notice may not be removed or altered from any source distribution. 52 | 53 | (this is the zlib license) 54 | */ 55 | 56 | #ifndef OSCPKT_HH 57 | #define OSCPKT_HH 58 | 59 | #ifndef _MSC_VER 60 | #include 61 | #else 62 | namespace oscpkt { 63 | typedef __int32 int32_t; 64 | typedef unsigned __int32 uint32_t; 65 | typedef __int64 int64_t; 66 | typedef unsigned __int64 uint64_t; 67 | } 68 | #endif 69 | #include 70 | #include 71 | #include 72 | #include 73 | #include 74 | 75 | #if defined(OSCPKT_OSTREAM_OUTPUT) || defined(OSCPKT_TEST) 76 | #include 77 | #endif 78 | 79 | namespace oscpkt { 80 | 81 | /** 82 | OSC timetag stuff, the highest 32-bit are seconds, the lowest are fraction of a second. 83 | */ 84 | class TimeTag { 85 | uint64_t v; 86 | public: 87 | TimeTag() : v(1) {} 88 | explicit TimeTag(uint64_t w): v(w) {} 89 | operator uint64_t() const { return v; } 90 | static TimeTag immediate() { return TimeTag(1); } 91 | }; 92 | 93 | /* the various types that we handle (OSC 1.0 specifies that INT32/FLOAT/STRING/BLOB are the bare minimum) */ 94 | enum { 95 | TYPE_TAG_TRUE = 'T', 96 | TYPE_TAG_FALSE = 'F', 97 | TYPE_TAG_INT32 = 'i', 98 | TYPE_TAG_INT64 = 'h', 99 | TYPE_TAG_FLOAT = 'f', 100 | TYPE_TAG_DOUBLE = 'd', 101 | TYPE_TAG_STRING = 's', 102 | TYPE_TAG_BLOB = 'b' 103 | }; 104 | 105 | /* a few utility functions follow.. */ 106 | 107 | // round to the next multiple of 4, works for size_t and pointer arguments 108 | template Type ceil4(Type p) { return (Type)((size_t(p) + 3)&(~size_t(3))); } 109 | 110 | // check that a memory area is zero padded until the next address which is a multiple of 4 111 | inline bool isZeroPaddingCorrect(const char *p) { 112 | const char *q = ceil4(p); 113 | for (;p < q; ++p) 114 | if (*p != 0) { return false; } 115 | return true; 116 | } 117 | 118 | // stuff for reading / writing POD ("Plain Old Data") variables to unaligned bytes. 119 | template union PodBytes { 120 | char bytes[sizeof(POD)]; 121 | POD value; 122 | }; 123 | 124 | inline bool isBigEndian() { // a compile-time constant would certainly improve performances.. 125 | PodBytes p; p.value = 0x12345678; 126 | return p.bytes[0] == 0x12; 127 | } 128 | 129 | /** read unaligned bytes into a POD type, assuming the bytes are a little endian representation */ 130 | template POD bytes2pod(const char *bytes) { 131 | PodBytes p; 132 | for (size_t i=0; i < sizeof(POD); ++i) { 133 | if (isBigEndian()) 134 | p.bytes[i] = bytes[i]; 135 | else 136 | p.bytes[i] = bytes[sizeof(POD) - i - 1]; 137 | } 138 | return p.value; 139 | } 140 | 141 | /** stored a POD type into an unaligned bytes array, using little endian representation */ 142 | template void pod2bytes(const POD value, char *bytes) { 143 | PodBytes p; p.value = value; 144 | for (size_t i=0; i < sizeof(POD); ++i) { 145 | if (isBigEndian()) 146 | bytes[i] = p.bytes[i]; 147 | else 148 | bytes[i] = p.bytes[sizeof(POD) - i - 1]; 149 | } 150 | } 151 | 152 | /** internal stuff, handles the dynamic storage with correct alignments to 4 bytes */ 153 | struct Storage { 154 | std::vector data; 155 | Storage() { data.reserve(200); } 156 | char *getBytes(size_t sz) { 157 | assert((data.size() & 3) == 0); 158 | if (data.size() + sz > data.capacity()) { data.reserve((data.size() + sz)*2); } 159 | size_t sz4 = ceil4(sz); 160 | size_t pos = data.size(); 161 | data.resize(pos + sz4); // resize will fill with zeros, so the zero padding is OK 162 | return &(data[pos]); 163 | } 164 | char *begin() { return data.size() ? &data.front() : 0; } 165 | char *end() { return begin() + size(); } 166 | const char *begin() const { return data.size() ? &data.front() : 0; } 167 | const char *end() const { return begin() + size(); } 168 | size_t size() const { return data.size(); } 169 | void assign(const char *beg, const char *end) { data.assign(beg, end); } 170 | void clear() { data.resize(0); } 171 | }; 172 | 173 | /** check if the path matches the supplied path pattern , according to the OSC spec pattern 174 | rules ('*' and '//' wildcards, '{}' alternatives, brackets etc) */ 175 | bool fullPatternMatch(const std::string &pattern, const std::string &path); 176 | /** check if the path matches the beginning of pattern */ 177 | bool partialPatternMatch(const std::string &pattern, const std::string &path); 178 | 179 | #if defined(OSCPKT_DEBUG) 180 | #define OSCPKT_SET_ERR(errcode) do { if (!err) { err = errcode; std::cerr << "set " #errcode << " at line " << __LINE__ << "\n"; } } while (0) 181 | #else 182 | #define OSCPKT_SET_ERR(errcode) do { if (!err) err = errcode; } while (0) 183 | #endif 184 | 185 | typedef enum { OK_NO_ERROR=0, 186 | // errors raised by the Message class: 187 | MALFORMED_ADDRESS_PATTERN, MALFORMED_TYPE_TAGS, MALFORMED_ARGUMENTS, UNHANDLED_TYPE_TAGS, 188 | // errors raised by ArgReader 189 | TYPE_MISMATCH, NOT_ENOUGH_ARG, PATTERN_MISMATCH, 190 | // errors raised by PacketReader/PacketWriter 191 | INVALID_BUNDLE, INVALID_PACKET_SIZE, BUNDLE_REQUIRED_FOR_MULTI_MESSAGES } ErrorCode; 192 | 193 | /** 194 | struct used to hold an OSC message that will be written or read. 195 | 196 | The list of arguments is exposed as a sort of queue. You "pop" 197 | arguments from the front of the queue when reading, you push 198 | arguments at the back of the queue when writing. 199 | 200 | Many functions return *this, so they can be chained: init("/foo").pushInt32(2).pushStr("kllk")... 201 | 202 | Example of use: 203 | 204 | creation of a message: 205 | @code 206 | msg.init("/foo").pushInt32(4).pushStr("bar"); 207 | @endcode 208 | reading a message, with error detection: 209 | @code 210 | if (msg.match("/foo/b*ar/plop")) { 211 | int i; std::string s; std::vector b; 212 | if (msg.arg().popInt32(i).popStr(s).popBlob(b).isOkNoMoreArgs()) { 213 | process message...; 214 | } else arguments mismatch; 215 | } 216 | @endcode 217 | */ 218 | class Message { 219 | TimeTag time_tag; 220 | std::string address; 221 | std::string type_tags; 222 | std::vector > arguments; // array of pairs (pos,size), pos being an index into the 'storage' array. 223 | Storage storage; // the arguments data is stored here 224 | ErrorCode err; 225 | public: 226 | /** ArgReader is used for popping arguments from a Message, holds a 227 | pointer to the original Message, and maintains a local error code */ 228 | class ArgReader { 229 | const Message *msg; 230 | ErrorCode err; 231 | size_t arg_idx; // arg index of the next arg that will be popped out. 232 | public: 233 | ArgReader(const Message &m, ErrorCode e = OK_NO_ERROR) : msg(&m), err(msg->getErr()), arg_idx(0) { 234 | if (e != OK_NO_ERROR && err == OK_NO_ERROR) err=e; 235 | } 236 | ArgReader(const ArgReader &other) : msg(other.msg), err(other.err), arg_idx(other.arg_idx) {} 237 | bool isBool() { return currentTypeTag() == TYPE_TAG_TRUE || currentTypeTag() == TYPE_TAG_FALSE; } 238 | bool isInt32() { return currentTypeTag() == TYPE_TAG_INT32; } 239 | bool isInt64() { return currentTypeTag() == TYPE_TAG_INT64; } 240 | bool isFloat() { return currentTypeTag() == TYPE_TAG_FLOAT; } 241 | bool isDouble() { return currentTypeTag() == TYPE_TAG_DOUBLE; } 242 | bool isStr() { return currentTypeTag() == TYPE_TAG_STRING; } 243 | bool isBlob() { return currentTypeTag() == TYPE_TAG_BLOB; } 244 | 245 | size_t nbArgRemaining() const { return msg->arguments.size() - arg_idx; } 246 | bool isOk() const { return err == OK_NO_ERROR; } 247 | operator bool() const { return isOk(); } // implicit bool conversion is handy here 248 | /** call this at the end of the popXXX() chain to make sure everything is ok and 249 | all arguments have been popped */ 250 | bool isOkNoMoreArgs() const { return err == OK_NO_ERROR && nbArgRemaining() == 0; } 251 | ErrorCode getErr() const { return err; } 252 | 253 | /** retrieve an int32 argument */ 254 | ArgReader &popInt32(int32_t &i) { return popPod(TYPE_TAG_INT32, i); } 255 | /** retrieve an int64 argument */ 256 | ArgReader &popInt64(int64_t &i) { return popPod(TYPE_TAG_INT64, i); } 257 | /** retrieve a single precision floating point argument */ 258 | ArgReader &popFloat(float &f) { return popPod(TYPE_TAG_FLOAT, f); } 259 | /** retrieve a double precision floating point argument */ 260 | ArgReader &popDouble(double &d) { return popPod(TYPE_TAG_DOUBLE, d); } 261 | /** retrieve a string argument (no check performed on its content, so it may contain any byte value except 0) */ 262 | ArgReader &popStr(std::string &s) { 263 | if (precheck(TYPE_TAG_STRING)) { 264 | s = argBeg(arg_idx++); 265 | } 266 | return *this; 267 | } 268 | /** retrieve a binary blob */ 269 | ArgReader &popBlob(std::vector &b) { 270 | if (precheck(TYPE_TAG_BLOB)) { 271 | b.assign(argBeg(arg_idx)+4, argEnd(arg_idx)); 272 | ++arg_idx; 273 | } 274 | return *this; 275 | } 276 | /** retrieve a boolean argument */ 277 | ArgReader &popBool(bool &b) { 278 | b = false; 279 | if (arg_idx >= msg->arguments.size()) OSCPKT_SET_ERR(NOT_ENOUGH_ARG); 280 | else if (currentTypeTag() == TYPE_TAG_TRUE) b = true; 281 | else if (currentTypeTag() == TYPE_TAG_FALSE) b = false; 282 | else OSCPKT_SET_ERR(TYPE_MISMATCH); 283 | ++arg_idx; 284 | return *this; 285 | } 286 | /** skip whatever comes next */ 287 | ArgReader &pop() { 288 | if (arg_idx >= msg->arguments.size()) OSCPKT_SET_ERR(NOT_ENOUGH_ARG); 289 | else ++arg_idx; 290 | return *this; 291 | } 292 | private: 293 | const char *argBeg(size_t idx) { 294 | if (err || idx >= msg->arguments.size()) return 0; 295 | else return msg->storage.begin() + msg->arguments[idx].first; 296 | } 297 | const char *argEnd(size_t idx) { 298 | if (err || idx >= msg->arguments.size()) return 0; 299 | else return msg->storage.begin() + msg->arguments[idx].first + msg->arguments[idx].second; 300 | } 301 | int currentTypeTag() { 302 | if (!err && arg_idx < msg->type_tags.size()) return msg->type_tags[arg_idx]; 303 | else OSCPKT_SET_ERR(NOT_ENOUGH_ARG); 304 | return -1; 305 | } 306 | template ArgReader &popPod(int tag, POD &v) { 307 | if (precheck(tag)) { 308 | v = bytes2pod(argBeg(arg_idx)); 309 | ++arg_idx; 310 | } else v = POD(0); 311 | return *this; 312 | } 313 | /* pre-check stuff before popping an argument from the message */ 314 | bool precheck(int tag) { 315 | if (arg_idx >= msg->arguments.size()) OSCPKT_SET_ERR(NOT_ENOUGH_ARG); 316 | else if (!err && currentTypeTag() != tag) OSCPKT_SET_ERR(TYPE_MISMATCH); 317 | return err == OK_NO_ERROR; 318 | } 319 | }; 320 | 321 | Message() { clear(); } 322 | Message(const std::string &s, TimeTag tt = TimeTag::immediate()) : time_tag(tt), address(s), err(OK_NO_ERROR) {} 323 | Message(const void *ptr, size_t sz, TimeTag tt = TimeTag::immediate()) { buildFromRawData(ptr, sz); time_tag = tt; } 324 | 325 | bool isOk() const { return err == OK_NO_ERROR; } 326 | ErrorCode getErr() const { return err; } 327 | 328 | /** return the type_tags string, with its initial ',' stripped. */ 329 | const std::string &typeTags() const { return type_tags; } 330 | /** retrieve the address pattern. If you want to follow to the whole OSC spec, you 331 | have to handle its matching rules for address specifications -- this file does 332 | not provide this functionality */ 333 | const std::string &addressPattern() const { return address; } 334 | TimeTag timeTag() const { return time_tag; } 335 | /** clear the message and start a new message with the supplied address and time_tag. */ 336 | Message &init(const std::string &addr, TimeTag tt = TimeTag::immediate()) { 337 | clear(); 338 | address = addr; time_tag = tt; 339 | if (address.empty() || address[0] != '/') OSCPKT_SET_ERR(MALFORMED_ADDRESS_PATTERN); 340 | return *this; 341 | } 342 | 343 | /** start a matching test. The typical use-case is to follow this by 344 | a sequence of calls to popXXX() and a final call to 345 | isOkNoMoreArgs() which will allow to check that everything went 346 | fine. For example: 347 | @code 348 | if (msg.match("/foo").popInt32(i).isOkNoMoreArgs()) { blah(i); } 349 | else if (msg.match("/bar").popStr(s).popInt32(i).isOkNoMoreArgs()) { plop(s,i); } 350 | else cerr << "unhandled message: " << msg << "\n"; 351 | @endcode 352 | */ 353 | ArgReader match(const std::string &test) const { 354 | return ArgReader(*this, fullPatternMatch(address.c_str(), test.c_str()) ? OK_NO_ERROR : PATTERN_MISMATCH); 355 | } 356 | /** return true if the 'test' path matched by the first characters of addressPattern(). 357 | For ex. ("/foo/bar").partialMatch("/foo/") is true */ 358 | ArgReader partialMatch(const std::string &test) const { 359 | return ArgReader(*this, partialPatternMatch(address.c_str(), test.c_str()) ? OK_NO_ERROR : PATTERN_MISMATCH); 360 | } 361 | ArgReader arg() const { return ArgReader(*this, OK_NO_ERROR); } 362 | 363 | /** build the osc message for raw data (the message will keep a copy of that data) */ 364 | void buildFromRawData(const void *ptr, size_t sz) { 365 | clear(); 366 | storage.assign((const char*)ptr, (const char*)ptr + sz); 367 | const char *address_beg = storage.begin(); 368 | const char *address_end = (const char*)memchr(address_beg, 0, storage.end()-address_beg); 369 | if (!address_end || !isZeroPaddingCorrect(address_end+1) || address_beg[0] != '/') { 370 | OSCPKT_SET_ERR(MALFORMED_ADDRESS_PATTERN); return; 371 | } else address.assign(address_beg, address_end); 372 | 373 | const char *type_tags_beg = ceil4(address_end+1); 374 | const char *type_tags_end = (const char*)memchr(type_tags_beg, 0, storage.end()-type_tags_beg); 375 | if (!type_tags_end || !isZeroPaddingCorrect(type_tags_end+1) || type_tags_beg[0] != ',') { 376 | OSCPKT_SET_ERR(MALFORMED_TYPE_TAGS); return; 377 | } else type_tags.assign(type_tags_beg+1, type_tags_end); // we do not copy the initial ',' 378 | 379 | const char *arg = ceil4(type_tags_end+1); assert(arg <= storage.end()); 380 | size_t iarg = 0; 381 | while (isOk() && iarg < type_tags.size()) { 382 | assert(arg <= storage.end()); 383 | size_t len = getArgSize(type_tags[iarg], arg); 384 | if (isOk()) arguments.push_back(std::make_pair(arg - storage.begin(), len)); 385 | arg += ceil4(len); ++iarg; 386 | } 387 | if (iarg < type_tags.size() || arg != storage.end()) { 388 | OSCPKT_SET_ERR(MALFORMED_ARGUMENTS); 389 | } 390 | } 391 | 392 | /* below are all the functions that serve when *writing* a message */ 393 | Message &pushBool(bool b) { 394 | type_tags += (b ? TYPE_TAG_TRUE : TYPE_TAG_FALSE); 395 | arguments.push_back(std::make_pair(storage.size(), storage.size())); 396 | return *this; 397 | } 398 | Message &pushInt32(int32_t i) { return pushPod(TYPE_TAG_INT32, i); } 399 | Message &pushInt64(int64_t h) { return pushPod(TYPE_TAG_INT64, h); } 400 | Message &pushFloat(float f) { return pushPod(TYPE_TAG_FLOAT, f); } 401 | Message &pushDouble(double d) { return pushPod(TYPE_TAG_DOUBLE, d); } 402 | Message &pushStr(const std::string &s) { 403 | assert(s.size() < 2147483647); // insane values are not welcome 404 | type_tags += TYPE_TAG_STRING; 405 | arguments.push_back(std::make_pair(storage.size(), s.size() + 1)); 406 | strcpy(storage.getBytes(s.size()+1), s.c_str()); 407 | return *this; 408 | } 409 | Message &pushBlob(void *ptr, size_t num_bytes) { 410 | assert(num_bytes < 2147483647); // insane values are not welcome 411 | type_tags += TYPE_TAG_BLOB; 412 | arguments.push_back(std::make_pair(storage.size(), num_bytes+4)); 413 | pod2bytes((int32_t)num_bytes, storage.getBytes(4)); 414 | if (num_bytes) 415 | memcpy(storage.getBytes(num_bytes), ptr, num_bytes); 416 | return *this; 417 | } 418 | 419 | /** reset the message to a clean state */ 420 | void clear() { 421 | address.clear(); type_tags.clear(); storage.clear(); arguments.clear(); 422 | err = OK_NO_ERROR; time_tag = TimeTag::immediate(); 423 | } 424 | 425 | /** write the raw message data (used by PacketWriter) */ 426 | void packMessage(Storage &s, bool write_size) const { 427 | if (!isOk()) return; 428 | size_t l_addr = address.size()+1, l_type = type_tags.size()+2; 429 | if (write_size) 430 | pod2bytes(uint32_t(ceil4(l_addr) + ceil4(l_type) + ceil4(storage.size())), s.getBytes(4)); 431 | strcpy(s.getBytes(l_addr), address.c_str()); 432 | strcpy(s.getBytes(l_type), ("," + type_tags).c_str()); 433 | if (storage.size()) 434 | memcpy(s.getBytes(storage.size()), const_cast(storage).begin(), storage.size()); 435 | } 436 | 437 | private: 438 | 439 | /* get the number of bytes occupied by the argument */ 440 | size_t getArgSize(int type, const char *p) { 441 | if (err) return 0; 442 | size_t sz = 0; 443 | assert(p >= storage.begin() && p <= storage.end()); 444 | switch (type) { 445 | case TYPE_TAG_TRUE: 446 | case TYPE_TAG_FALSE: sz = 0; break; 447 | case TYPE_TAG_INT32: 448 | case TYPE_TAG_FLOAT: sz = 4; break; 449 | case TYPE_TAG_INT64: 450 | case TYPE_TAG_DOUBLE: sz = 8; break; 451 | case TYPE_TAG_STRING: { 452 | const char *q = (const char*)memchr(p, 0, storage.end()-p); 453 | if (!q) OSCPKT_SET_ERR(MALFORMED_ARGUMENTS); 454 | else sz = (q-p)+1; 455 | } break; 456 | case TYPE_TAG_BLOB: { 457 | if (p == storage.end()) { OSCPKT_SET_ERR(MALFORMED_ARGUMENTS); return 0; } 458 | sz = 4+bytes2pod(p); 459 | } break; 460 | default: { 461 | OSCPKT_SET_ERR(UNHANDLED_TYPE_TAGS); return 0; 462 | } break; 463 | } 464 | if (p+sz > storage.end() || /* string or blob too large.. */ 465 | p+sz < p /* or even blob so large that it did overflow */) { 466 | OSCPKT_SET_ERR(MALFORMED_ARGUMENTS); return 0; 467 | } 468 | if (!isZeroPaddingCorrect(p+sz)) { OSCPKT_SET_ERR(MALFORMED_ARGUMENTS); return 0; } 469 | return sz; 470 | } 471 | 472 | template Message &pushPod(int tag, POD v) { 473 | type_tags += (char)tag; 474 | arguments.push_back(std::make_pair(storage.size(), sizeof(POD))); 475 | pod2bytes(v, storage.getBytes(sizeof(POD))); 476 | return *this; 477 | } 478 | 479 | #ifdef OSCPKT_OSTREAM_OUTPUT 480 | friend std::ostream &operator<<(std::ostream &os, const Message &msg) { 481 | os << "osc_address: '" << msg.address << "', types: '" << msg.type_tags << "', timetag=" << msg.time_tag << ", args=["; 482 | Message::ArgReader arg(msg); 483 | while (arg.nbArgRemaining() && arg.isOk()) { 484 | if (arg.isBool()) { bool b; arg.popBool(b); os << (b?"True":"False"); } 485 | else if (arg.isInt32()) { int32_t i; arg.popInt32(i); os << i; } 486 | else if (arg.isInt64()) { int64_t h; arg.popInt64(h); os << h << "ll"; } 487 | else if (arg.isFloat()) { float f; arg.popFloat(f); os << f << "f"; } 488 | else if (arg.isDouble()) { double d; arg.popDouble(d); os << d; } 489 | else if (arg.isStr()) { std::string s; arg.popStr(s); os << "'" << s << "'"; } 490 | else if (arg.isBlob()) { std::vector b; arg.popBlob(b); os << "Blob " << b.size() << " bytes"; } 491 | else { 492 | assert(0); // I forgot a case.. 493 | } 494 | if (arg.nbArgRemaining()) os << ", "; 495 | } 496 | if (!arg.isOk()) { os << " ERROR#" << arg.getErr(); } 497 | os << "]"; 498 | return os; 499 | } 500 | #endif 501 | }; 502 | 503 | /** 504 | parse an OSC packet and extracts the embedded OSC messages. 505 | */ 506 | class PacketReader { 507 | public: 508 | PacketReader() { err = OK_NO_ERROR; } 509 | /** pointer and size of the osc packet to be parsed. */ 510 | PacketReader(const void *ptr, size_t sz) { init(ptr, sz); } 511 | 512 | void init(const void *ptr, size_t sz) { 513 | err = OK_NO_ERROR; messages.clear(); 514 | if ((sz%4) == 0) { 515 | parse((const char*)ptr, (const char *)ptr+sz, TimeTag::immediate()); 516 | } else OSCPKT_SET_ERR(INVALID_PACKET_SIZE); 517 | it_messages = messages.begin(); 518 | } 519 | 520 | /** extract the next osc message from the packet. return 0 when all messages have been read, or in case of error. */ 521 | Message *popMessage() { 522 | if (!err && !messages.empty() && it_messages != messages.end()) return &*it_messages++; 523 | else return 0; 524 | } 525 | bool isOk() const { return err == OK_NO_ERROR; } 526 | ErrorCode getErr() const { return err; } 527 | 528 | private: 529 | std::list messages; 530 | std::list::iterator it_messages; 531 | ErrorCode err; 532 | 533 | void parse(const char *beg, const char *end, TimeTag time_tag) { 534 | assert(beg <= end && !err); assert(((end-beg)%4)==0); 535 | 536 | if (beg == end) return; 537 | if (*beg == '#') { 538 | /* it's a bundle */ 539 | if (end - beg >= 20 540 | && memcmp(beg, "#bundle\0", 8) == 0) { 541 | TimeTag time_tag2(bytes2pod(beg+8)); 542 | const char *pos = beg + 16; 543 | do { 544 | uint32_t sz = bytes2pod(pos); pos += 4; 545 | if ((sz&3) != 0 || pos + sz > end || pos+sz < pos) { 546 | OSCPKT_SET_ERR(INVALID_BUNDLE); 547 | } else { 548 | parse(pos, pos+sz, time_tag2); 549 | pos += sz; 550 | } 551 | } while (!err && pos != end); 552 | } else { 553 | OSCPKT_SET_ERR(INVALID_BUNDLE); 554 | } 555 | } else { 556 | messages.push_back(Message(beg, end-beg, time_tag)); 557 | if (!messages.back().isOk()) OSCPKT_SET_ERR(messages.back().getErr()); 558 | } 559 | } 560 | }; 561 | 562 | 563 | /** 564 | Assemble messages into an OSC packet. Example of use: 565 | @code 566 | PacketWriter pkt; 567 | Message msg; 568 | pkt.startBundle(); 569 | pkt.addMessage(msg.init("/foo").pushBool(true).pushStr("plop").pushFloat(3.14f)); 570 | pkt.addMessage(msg.init("/bar").pushBool(false)); 571 | pkt.endBundle(); 572 | if (pkt.isOk()) { 573 | send(pkt.data(), pkt.size()); 574 | } 575 | @endcode 576 | */ 577 | class PacketWriter { 578 | public: 579 | PacketWriter() { init(); } 580 | PacketWriter &init() { err = OK_NO_ERROR; storage.clear(); bundles.clear(); return *this; } 581 | 582 | /** begin a new bundle. If you plan to pack more than one message in the Osc packet, you have to 583 | put them in a bundle. Nested bundles inside bundles are also allowed. */ 584 | PacketWriter &startBundle(TimeTag ts = TimeTag::immediate()) { 585 | char *p; 586 | if (bundles.size()) storage.getBytes(4); // hold the bundle size 587 | p = storage.getBytes(8); strcpy(p, "#bundle"); bundles.push_back(p - storage.begin()); 588 | p = storage.getBytes(8); pod2bytes(ts, p); 589 | return *this; 590 | } 591 | /** close the current bundle. */ 592 | PacketWriter &endBundle() { 593 | if (bundles.size()) { 594 | if (storage.size() - bundles.back() == 16) { 595 | pod2bytes(0, storage.getBytes(4)); // the 'empty bundle' case, not very elegant 596 | } 597 | if (bundles.size()>1) { // no size stored for the top-level bundle 598 | pod2bytes(uint32_t(storage.size() - bundles.back()), storage.begin() + bundles.back()-4); 599 | } 600 | bundles.pop_back(); 601 | } else OSCPKT_SET_ERR(INVALID_BUNDLE); 602 | return *this; 603 | } 604 | 605 | /** insert an Osc message into the current bundle / packet. 606 | */ 607 | PacketWriter &addMessage(const Message &msg) { 608 | if (storage.size() != 0 && bundles.empty()) OSCPKT_SET_ERR(BUNDLE_REQUIRED_FOR_MULTI_MESSAGES); 609 | else msg.packMessage(storage, bundles.size()>0); 610 | if (!msg.isOk()) OSCPKT_SET_ERR(msg.getErr()); 611 | return *this; 612 | } 613 | 614 | /** the error flag will be raised if an opened bundle is not closed, or if more than one message is 615 | inserted in the packet without a bundle */ 616 | bool isOk() { return err == OK_NO_ERROR; } 617 | ErrorCode getErr() { return err; } 618 | 619 | /** return the number of bytes of the osc packet -- will always be a 620 | multiple of 4 -- returns 0 if the construction of the packet has 621 | failed. */ 622 | uint32_t packetSize() { return err ? 0 : (uint32_t)storage.size(); } 623 | 624 | /** return the bytes of the osc packet (NULL if the construction of the packet has failed) */ 625 | char *packetData() { return err ? 0 : storage.begin(); } 626 | private: 627 | std::vector bundles; // hold the position in the storage array of the beginning marker of each bundle 628 | Storage storage; 629 | ErrorCode err; 630 | }; 631 | 632 | // see the OSC spec for the precise pattern matching rules 633 | inline const char *internalPatternMatch(const char *pattern, const char *path) { 634 | while (*pattern) { 635 | const char *p = pattern; 636 | if (*p == '?' && *path) { ++p; ++path; } 637 | else if (*p == '[' && *path) { // bracketted range, e.g. [a-zABC] 638 | ++p; 639 | bool reverse = false; 640 | if (*p == '!') { reverse = true; ++p; } 641 | bool match = reverse; 642 | for (; *p && *p != ']'; ++p) { 643 | char c0 = *p, c1 = c0; 644 | if (p[1] == '-' && p[2] && p[2] != ']') { p += 2; c1 = *p; } 645 | if (*path >= c0 && *path <= c1) { match = !reverse; } 646 | } 647 | if (!match || *p != ']') return pattern; 648 | ++p; ++path; 649 | } else if (*p == '*') { // wildcard '*' 650 | while (*p == '*') ++p; 651 | const char *best = 0; 652 | while (true) { 653 | const char *ret = internalPatternMatch(p, path); 654 | if (ret && ret > best) best = ret; 655 | if (*path == 0 || *path == '/') break; 656 | else ++path; 657 | } 658 | return best; 659 | } else if (*p == '/' && *(p+1) == '/') { // the super-wildcard '//' 660 | while (*(p+1)=='/') ++p; 661 | const char *best = 0; 662 | while (true) { 663 | const char *ret = internalPatternMatch(p, path); 664 | if (ret && ret > best) best = ret; 665 | if (*path == 0) break; 666 | if (*path == 0 || (path = strchr(path+1, '/')) == 0) break; 667 | } 668 | return best; 669 | } else if (*p == '{') { // braced list {foo,bar,baz} 670 | const char *end = strchr(p, '}'), *q; 671 | if (!end) return 0; // syntax error in brace list.. 672 | bool match = false; 673 | do { 674 | ++p; 675 | q = strchr(p, ','); 676 | if (q == 0 || q > end) q = end; 677 | if (strncmp(p, path, q-p)==0) { 678 | path += (q-p); p = end+1; match = true; 679 | } else p=q; 680 | } while (q != end && !match); 681 | if (!match) return pattern; 682 | } else if (*p == *path) { ++p; ++path; } // any other character 683 | else break; 684 | pattern = p; 685 | } 686 | return (*path == 0 ? pattern : 0); 687 | } 688 | 689 | inline bool partialPatternMatch(const std::string &pattern, const std::string &test) { 690 | const char *q = internalPatternMatch(pattern.c_str(), test.c_str()); 691 | return q != 0; 692 | } 693 | 694 | inline bool fullPatternMatch(const std::string &pattern, const std::string &test) { 695 | const char *q = internalPatternMatch(pattern.c_str(), test.c_str()); 696 | return q && *q == 0; 697 | } 698 | 699 | } // namespace oscpkt 700 | 701 | #endif // OSCPKT_HH 702 | -------------------------------------------------------------------------------- /lib/Trill.cpp: -------------------------------------------------------------------------------- 1 | #include "Trill.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | constexpr uint8_t Trill::speedValues[4]; 8 | #define MAX_TOUCH_1D_OR_2D (((device_type_ == SQUARE || device_type_ == HEX) ? kMaxTouchNum2D : kMaxTouchNum1D)) 9 | 10 | enum { 11 | kCentroidLengthDefault = 20, 12 | kCentroidLengthRing = 24, 13 | kCentroidLength2D = 32, 14 | kRawLength = 60, 15 | }; 16 | 17 | enum { 18 | kCommandNone = 0, 19 | kCommandMode = 1, 20 | kCommandScanSettings = 2, 21 | kCommandPrescaler = 3, 22 | kCommandNoiseThreshold = 4, 23 | kCommandIdac = 5, 24 | kCommandBaselineUpdate = 6, 25 | kCommandMinimumSize = 7, 26 | kCommandEventMode = 9, 27 | kCommandChannelMaskLow = 10, 28 | kCommandChannelMaskHigh = 11, 29 | kCommandReset = 12, 30 | kCommandFormat = 13, 31 | kCommandTimerPeriod = 14, 32 | kCommandScanTrigger = 15, 33 | kCommandAutoScanInterval = 16, 34 | kCommandAck = 254, 35 | kCommandIdentify = 255 36 | }; 37 | 38 | enum { 39 | kOffsetCommand = 0, 40 | kOffsetStatusByte = 3, 41 | kOffsetChannelData = 4, 42 | }; 43 | 44 | enum { 45 | kNumChannelsBar = 26, 46 | kNumChannelsRing = 30, 47 | kNumChannelsMax = 30, 48 | }; 49 | 50 | enum { 51 | kMaxTouchNum1D = 5, 52 | kMaxTouchNum2D = 4 53 | }; 54 | 55 | struct TrillDefaults 56 | { 57 | TrillDefaults(std::string name, Trill::Mode mode, float noiseThreshold, uint8_t address, uint8_t prescaler) : 58 | name(name), mode(mode), noiseThreshold(noiseThreshold), 59 | address(address), prescaler(prescaler) {} 60 | std::string name; 61 | Trill::Mode mode; 62 | float noiseThreshold; 63 | uint8_t address; 64 | int8_t prescaler; 65 | }; 66 | 67 | const float defaultThreshold = 0x28 / 4096.f; 68 | static const std::map trillDefaults = { 69 | {Trill::NONE, TrillDefaults("No device", Trill::AUTO, 0, 0xFF, -1)}, 70 | {Trill::ANY, TrillDefaults("Unknown device", Trill::AUTO, 0, 0xFF, -1)}, 71 | {Trill::BAR, TrillDefaults("Bar", Trill::CENTROID, defaultThreshold, 0x20, 2)}, 72 | {Trill::SQUARE, TrillDefaults("Square", Trill::CENTROID, defaultThreshold, 0x28, 1)}, 73 | {Trill::CRAFT, TrillDefaults("Craft", Trill::DIFF, defaultThreshold, 0x30, 1)}, 74 | {Trill::RING, TrillDefaults("Ring", Trill::CENTROID, defaultThreshold, 0x38, 2)}, 75 | {Trill::HEX, TrillDefaults("Hex", Trill::CENTROID, defaultThreshold, 0x40, 1)}, 76 | {Trill::FLEX, TrillDefaults("Flex", Trill::CENTROID, 0.03, 0x48, 4)}, 77 | }; 78 | 79 | static const std::map trillModes = { 80 | {Trill::AUTO, "Auto"}, 81 | {Trill::CENTROID, "Centroid"}, 82 | {Trill::RAW, "Raw"}, 83 | {Trill::BASELINE, "Baseline"}, 84 | {Trill::DIFF, "Diff"}, 85 | }; 86 | 87 | struct trillRescaleFactors_t { 88 | float pos; 89 | float posH; 90 | float size; 91 | }; 92 | 93 | static const std::vector trillRescaleFactors ={ 94 | {.pos = 1, .posH = 0, .size = 1}, // ANY = 0, 95 | {.pos = 3200, .posH = 0, .size = 4566}, // BAR = 1, 96 | {.pos = 1792, .posH = 1792, .size = 3780}, // SQUARE = 2, 97 | {.pos = 4096, .posH = 0, .size = 1}, // CRAFT = 3, 98 | {.pos = 3584, .posH = 0, .size = 5000}, // RING = 4, 99 | {.pos = 1920, .posH = 1664, .size = 4000}, // HEX = 5, 100 | {.pos = 3712, .posH = 0, .size = 1200}, // FLEX = 6, 101 | }; 102 | 103 | struct TrillStatusByte { 104 | uint8_t frameId : 6; 105 | uint8_t activity : 1; 106 | uint8_t initialised : 1; 107 | static TrillStatusByte parse(uint8_t statusByte) 108 | { 109 | return *(TrillStatusByte*)(&statusByte); 110 | } 111 | }; 112 | static_assert(1 == sizeof(TrillStatusByte), "size and layout of TrillStatusByte must match the Trill firmware"); 113 | static_assert(kOffsetStatusByte + sizeof(TrillStatusByte) == kOffsetChannelData, "Assume that channel data is available immediately after the statusByte"); 114 | 115 | Trill::Trill(){} 116 | 117 | Trill::Trill(unsigned int i2c_bus, Device device, uint8_t i2c_address) { 118 | setup(i2c_bus, device, i2c_address); 119 | } 120 | 121 | void Trill::updateChannelMask(uint32_t mask) 122 | { 123 | channelMask = (mask & ((1 << getDefaultNumChannels()) - 1)); 124 | numChannels = std::min(int(getDefaultNumChannels()), __builtin_popcount(channelMask)); 125 | } 126 | 127 | int Trill::setup(unsigned int i2c_bus, Device device, uint8_t i2c_address) 128 | { 129 | dataBuffer.resize(0); 130 | rawData.resize(0); 131 | rawData.resize(kNumChannelsMax); 132 | address = 0; 133 | frameId = 0; 134 | device_type_ = NONE; 135 | TrillDefaults defaults = trillDefaults.at(device); 136 | if(ANY == device && 255 == i2c_address) { 137 | auto devs = probeRange(i2c_bus, 1); 138 | if(devs.size()) { 139 | const auto& d = devs[0]; 140 | device = d.first; 141 | i2c_address = d.second; 142 | } else { 143 | fprintf(stderr, "No Trill device found on I2C bus %d\n", i2c_bus); 144 | return 2; 145 | } 146 | } 147 | 148 | if(128 <= i2c_address) 149 | i2c_address = defaults.address; 150 | 151 | if(128 <= i2c_address) { 152 | fprintf(stderr, "Unknown default address for device type %s\n", 153 | defaults.name.c_str()); 154 | return -2; 155 | } 156 | if(initI2C_RW(i2c_bus, i2c_address, -1)) { 157 | fprintf(stderr, "Unable to initialise I2C communication\n"); 158 | return 1; 159 | } 160 | // until we find out the actual, disable the version check and allow 161 | // for silent failure of commands 162 | enableVersionCheck = false; 163 | reset(); // this is a time-consuming NOP for fw < 3 164 | 165 | // disable scanning so communication is faster 166 | // NOTE: ignoring return of setScanTrigger(): for fw < 3, it will 167 | // allegedly fail for lack of ack 168 | setScanTrigger(kScanTriggerDisabled); 169 | if(identify() != 0) { 170 | fprintf(stderr, "Unable to identify device\n"); 171 | return 2; 172 | } 173 | if(ANY != device && device_type_ != device) { 174 | fprintf(stderr, "Wrong device type detected. `%s` was requested " 175 | "but `%s` was detected on bus %d at address %#x(%d).\n", 176 | defaults.name.c_str(), 177 | trillDefaults.at(device_type_).name.c_str(), 178 | i2c_bus, i2c_address, i2c_address 179 | ); 180 | device_type_ = NONE; 181 | return -3; 182 | } 183 | // if the device was unknown it will have changed by now 184 | defaults = trillDefaults.at(device_type_); 185 | // now we have a proper version, we can check against it 186 | enableVersionCheck = true; 187 | 188 | constexpr uint32_t defaultChannelMask = 0xffffffff; 189 | if(firmware_version_ >= 3) 190 | { 191 | setChannelMask(defaultChannelMask); 192 | } else { 193 | // only keep track of it for internal purposes 194 | updateChannelMask(defaultChannelMask); 195 | } 196 | 197 | Mode mode = defaults.mode; 198 | if(setMode(mode) != 0) { 199 | fprintf(stderr, "Unable to set mode\n"); 200 | return 3; 201 | } 202 | 203 | int8_t prescaler = defaults.prescaler; 204 | if(prescaler >= 0) 205 | { 206 | if(setPrescaler(prescaler)){ 207 | fprintf(stderr, "Unable to set prescaler\n"); 208 | return 8; 209 | } 210 | } 211 | 212 | if(setScanSettings(0, 12)){ 213 | fprintf(stderr, "Unable to set scan settings\n"); 214 | return 7; 215 | } 216 | 217 | if(updateBaseline() != 0) { 218 | fprintf(stderr, "Unable to update baseline\n"); 219 | return 6; 220 | } 221 | 222 | if(setNoiseThreshold(defaults.noiseThreshold)) { 223 | fprintf(stderr, "Unable to update baseline\n"); 224 | return 9; 225 | } 226 | 227 | address = i2c_address; 228 | readErrorOccurred = false; 229 | 230 | if(firmware_version_ >= 3) 231 | { 232 | if(setScanTrigger(kScanTriggerI2c)) 233 | return 1; 234 | } 235 | return 0; 236 | } 237 | 238 | Trill::Device Trill::probe(unsigned int i2c_bus, uint8_t i2c_address) 239 | { 240 | Trill t; 241 | t.quiet = true; 242 | if(t.initI2C_RW(i2c_bus, i2c_address, -1)) { 243 | return Trill::NONE; 244 | } 245 | if(t.identify() != 0) { 246 | return Trill::NONE; 247 | } 248 | return t.device_type_; 249 | } 250 | 251 | std::vector > Trill::probeRange(unsigned int i2c_bus, size_t maxCount) 252 | { 253 | std::vector< std::pair > devs; 254 | if(0 == maxCount) 255 | maxCount = std::numeric_limits::max(); 256 | // probe the valid address range on the bus to find a valid device 257 | for(uint8_t n = 0x20; n <= 0x50 && devs.size() <= maxCount; ++n) { 258 | Device device = probe(i2c_bus, n); 259 | if(device != NONE) 260 | devs.push_back({device, n}); 261 | } 262 | return devs; 263 | } 264 | 265 | Trill::~Trill() { 266 | closeI2C(); 267 | } 268 | 269 | const std::string& Trill::getNameFromDevice(Device device) 270 | { 271 | __try { 272 | return trillDefaults.at(device).name; 273 | } __catch (std::exception e) { 274 | return trillDefaults.at(Device::ANY).name; 275 | } 276 | } 277 | 278 | static bool strCmpIns(const std::string& str1, const std::string& str2) 279 | { 280 | bool equal = true; 281 | if(str1.size() == str2.size()) { 282 | for(unsigned int n = 0; n < str1.size(); ++n) { 283 | if(std::tolower(str1[n]) != std::tolower(str2[n])) { 284 | equal = false; 285 | break; 286 | } 287 | } 288 | } else 289 | equal = false; 290 | return equal; 291 | } 292 | 293 | Trill::Device Trill::getDeviceFromName(const std::string& name) 294 | { 295 | for(auto& td : trillDefaults) 296 | { 297 | Device device = td.first; 298 | const std::string& str2 = trillDefaults.at(device).name; 299 | if(strCmpIns(name, str2)) 300 | return Device(device); 301 | } 302 | return Trill::ANY; 303 | } 304 | 305 | const std::string& Trill::getNameFromMode(Mode mode) 306 | { 307 | __try { 308 | return trillModes.at(mode); 309 | } __catch (std::exception e) { 310 | return trillModes.at(Mode::AUTO); 311 | } 312 | } 313 | 314 | Trill::Mode Trill::getModeFromName(const std::string& name) 315 | { 316 | for(auto& m : trillModes) 317 | { 318 | const std::string& str2 = m.second; 319 | if(strCmpIns(name, str2)) 320 | return m.first; 321 | } 322 | return Trill::AUTO; 323 | } 324 | 325 | // macros to automatically print method names. Using gcc-specific __PRETTY_FUNCTION__. 326 | #define WRITE_COMMAND_BUF(data) writeCommandAndHandle(data, sizeof(data), __PRETTY_FUNCTION__) 327 | #define WRITE_COMMAND(command) writeCommandAndHandle(command, __PRETTY_FUNCTION__) 328 | #define READ_BYTES_FROM(offset,data,size) readBytesFrom(offset, data, size, __PRETTY_FUNCTION__) 329 | #define READ_BYTE_FROM(offset,byte) readBytesFrom(offset, byte, __PRETTY_FUNCTION__) 330 | 331 | int Trill::writeCommandAndHandle(i2c_char_t command, const char* name) { 332 | return writeCommandAndHandle(&command, sizeof(command), name); 333 | } 334 | 335 | static void printErrno(int ret) 336 | { 337 | if(-1 == ret) 338 | fprintf(stderr, "errno %d, %s.\n", errno, strerror(errno)); 339 | } 340 | int Trill::writeCommandAndHandle(const i2c_char_t* data, size_t size, const char* name) { 341 | constexpr size_t kMaxCommandBytes = 3; 342 | if(size > kMaxCommandBytes) 343 | { 344 | fprintf(stderr, "Trill: cannot write more than 3 bytes to the device\n"); 345 | return -1; 346 | } 347 | i2c_char_t buf[1 + kMaxCommandBytes]; 348 | buf[0] = kOffsetCommand; 349 | for(size_t n = 0; n < size; ++n) 350 | buf[n + 1] = data[n]; 351 | int bytesToWrite = size + 1; 352 | if(verbose) { 353 | printf("Writing %s :", name); 354 | for(ssize_t n = 1; n < bytesToWrite; ++n) 355 | printf("%d ", buf[n]); 356 | printf("\n"); 357 | } 358 | int ret = writeBytes(buf, bytesToWrite); 359 | if(ret != bytesToWrite) 360 | { 361 | if(!quiet) 362 | fprintf(stderr, "Trill: failed to write command \"%s\"; ret: %d, errno: %d, %s.\n", name, ret, errno, strerror(errno)); 363 | return 1; 364 | } 365 | currentReadOffset = buf[0]; 366 | if(kCommandReset == buf[1]) 367 | return usleep(500000); // it won't ack after reset ... (TODO: should it?) 368 | else 369 | return waitForAck(buf[1], name); 370 | } 371 | 372 | int Trill::readBytesFrom(const uint8_t offset, i2c_char_t& byte, const char* name) 373 | { 374 | return readBytesFrom(offset, &byte, sizeof(byte), name); 375 | } 376 | 377 | int Trill::readBytesFrom(const uint8_t offset, i2c_char_t* data, size_t size, const char* name) 378 | { 379 | if(offset != currentReadOffset) 380 | { 381 | int ret = writeBytes(&offset, sizeof(offset)); 382 | if(ret != sizeof(offset)) 383 | { 384 | if(!quiet) 385 | { 386 | fprintf(stderr, "%s: error while setting read offset\n", name); 387 | printErrno(ret); 388 | } 389 | return 1; 390 | } 391 | currentReadOffset = offset; 392 | usleep(commandSleepTime); 393 | } 394 | ssize_t bytesRead = readBytes(data, size); 395 | if (bytesRead != ssize_t(size)) 396 | { 397 | fprintf(stderr, "%s: failed to read %zd bytes. ret: %zd\n", name, size, bytesRead); 398 | printErrno(bytesRead); 399 | return 1; 400 | } 401 | return 0; 402 | } 403 | 404 | int Trill::waitForAck(const uint8_t command, const char* name) 405 | { 406 | if(firmware_version_ < 3) { 407 | // old or unknown firmware, use old sleep time for bw compatibility 408 | usleep(10000); 409 | return 0; 410 | } 411 | size_t bytesToRead; 412 | if(verbose) 413 | bytesToRead = 3; 414 | else 415 | bytesToRead = 1; 416 | i2c_char_t buf[bytesToRead]; 417 | unsigned int sleep = commandSleepTime; 418 | unsigned int totalSleep = 0; 419 | while(totalSleep < 200000) 420 | { 421 | usleep(sleep); 422 | if(readBytesFrom(kOffsetCommand, buf, sizeof(buf), name)) 423 | return 1; 424 | if(kCommandAck == buf[0]) 425 | { 426 | // The device places the received command number in the 427 | // second byte and a command counter in the third byte. 428 | // If verbose, those are read and can be inspected for 429 | // debugging purposes. 430 | verbose && printf("Ack'ed %d(%d) with %d %d %d\n", command, cmdCounter, buf[0], buf[1], buf[2]); 431 | if(verbose && (kCommandIdentify != command) && (buf[1] != command || buf[2] != cmdCounter)) { 432 | printf("^^^^^ reset cmdCounter\n"); 433 | cmdCounter = buf[2]; 434 | } else { 435 | cmdCounter++; 436 | return 0; 437 | } 438 | } 439 | verbose && printf("sleep %d: %d %d %d\n", sleep, buf[0], buf[1], buf[2]); 440 | totalSleep += sleep; 441 | sleep *= 2; 442 | if(!sleep) // avoid infinite loop in case we are told not to wait for ack 443 | break; 444 | } 445 | fprintf(stderr, "%s: failed to read ack for command %d\n",name, command); 446 | return 1; 447 | } 448 | 449 | #define REQUIRE_FW_AT_LEAST(num) \ 450 | if(enableVersionCheck && firmware_version_ < num) \ 451 | { \ 452 | fprintf(stderr, "%s unsupported with firmware version %d, requires %d\n", __PRETTY_FUNCTION__, firmware_version_, num); \ 453 | return 1; \ 454 | } 455 | 456 | int Trill::identify() { 457 | // NOTE: ignoring return of WRITE_COMMAND(): for fw < 3, it will 458 | // allegedly fail for lack of ack 459 | WRITE_COMMAND(kCommandIdentify); 460 | i2c_char_t rbuf[3]; 461 | if(READ_BYTES_FROM(kOffsetCommand, rbuf, sizeof(rbuf))) 462 | { 463 | device_type_ = NONE; 464 | return -1; 465 | } 466 | 467 | // if we read back just zeros, we assume the device did not respond 468 | if(0 == rbuf[1]) { 469 | device_type_ = NONE; 470 | return -1; 471 | } 472 | Device readDeviceType = (Device)rbuf[1]; 473 | // if we do not recognize the device type, we also return an error 474 | if(trillDefaults.find(readDeviceType) == trillDefaults.end()) { 475 | device_type_ = NONE; 476 | return -1; 477 | } 478 | device_type_ = readDeviceType; 479 | firmware_version_ = rbuf[2]; 480 | 481 | return 0; 482 | } 483 | 484 | void Trill::updateRescale() 485 | { 486 | enum { kRescaleFactorsComputedAtBits = 12 }; 487 | float scale = (1 << (16 - numBits)) / float(1 << (16 - kRescaleFactorsComputedAtBits)); 488 | posRescale = 1.f / trillRescaleFactors[device_type_].pos; 489 | posHRescale = 1.f / trillRescaleFactors[device_type_].posH; 490 | sizeRescale = scale / trillRescaleFactors[device_type_].size; 491 | rawRescale = 1.f / (1 << numBits); 492 | } 493 | 494 | void Trill::printDetails() 495 | { 496 | printf("Device type: %s (%d)\n", getNameFromDevice(device_type_).c_str(), deviceType()); 497 | printf("Address: %#x\n", address); 498 | printf("Firmware version: %d\n", firmwareVersion()); 499 | } 500 | 501 | void Trill::setVerbose(int verbose) 502 | { 503 | this->verbose = verbose; 504 | } 505 | 506 | int Trill::setMode(Mode mode) { 507 | if(AUTO == mode) 508 | mode = trillDefaults.at(device_type_).mode; 509 | i2c_char_t buf[] = { kCommandMode, (i2c_char_t)mode }; 510 | if(WRITE_COMMAND_BUF(buf)) 511 | return 1; 512 | mode_ = mode; 513 | return 0; 514 | } 515 | 516 | int Trill::setScanSettings(uint8_t speed, uint8_t num_bits) { 517 | if(speed > 3) 518 | speed = 3; 519 | if(num_bits < 9) 520 | num_bits = 9; 521 | if(num_bits > 16) 522 | num_bits = 16; 523 | i2c_char_t buf[] = { kCommandScanSettings, speed, num_bits }; 524 | if(WRITE_COMMAND_BUF(buf)) 525 | return 1; 526 | numBits = num_bits; 527 | updateRescale(); 528 | return 0; 529 | } 530 | 531 | int Trill::setPrescaler(uint8_t prescaler) { 532 | i2c_char_t buf[] = { kCommandPrescaler, prescaler }; 533 | return WRITE_COMMAND_BUF(buf); 534 | } 535 | 536 | int Trill::setNoiseThreshold(float threshold) { 537 | threshold = threshold * (1 << numBits); 538 | if(threshold > 255) 539 | threshold = 255; 540 | if(threshold < 0) 541 | threshold = 0; 542 | i2c_char_t thByte = i2c_char_t(threshold + 0.5); 543 | i2c_char_t buf[] = { kCommandNoiseThreshold, thByte }; 544 | return WRITE_COMMAND_BUF(buf); 545 | } 546 | 547 | 548 | int Trill::setIDACValue(uint8_t value) { 549 | i2c_char_t buf[] = { kCommandIdac, value }; 550 | return WRITE_COMMAND_BUF(buf); 551 | } 552 | 553 | int Trill::setMinimumTouchSize(float minSize) { 554 | uint16_t size; 555 | float maxMinSize = (1<<16) - 1; 556 | minSize /= sizeRescale; 557 | // clipping to the max value we can transmit 558 | if(minSize > maxMinSize) 559 | minSize = maxMinSize; 560 | size = minSize; 561 | i2c_char_t buf[] = { kCommandMinimumSize, (i2c_char_t)(size >> 8), (i2c_char_t)(size & 0xFF) }; 562 | return WRITE_COMMAND_BUF(buf); 563 | } 564 | 565 | int Trill::setTimerPeriod(float ms) { 566 | if(firmware_version_ >= 3) { 567 | if(ms < 0) 568 | ms = 0; 569 | const float kMaxMs = 255 * 255 / 32.f; 570 | if(ms > kMaxMs) 571 | ms = kMaxMs; 572 | // Start from a clock period of 1ms (32 cycles of the 32kHz clock) 573 | uint32_t period = 32; 574 | uint32_t ticks = ms + 0.5f; // round 575 | while(ticks > 255) { 576 | period *= 2; 577 | ticks /= 2; 578 | } 579 | if(period > 255) { 580 | // shouldn't get here 581 | fprintf(stderr, "Trill:setTimerPeriod(): the requested %f ms cannot be achieved. Using %lu instead\n", ms, (unsigned long)(period * ticks / 32)); 582 | period = 255; 583 | } 584 | i2c_char_t buf[] = { kCommandTimerPeriod, i2c_char_t(period), i2c_char_t(ticks) }; 585 | if(WRITE_COMMAND_BUF(buf)) 586 | return 1; 587 | } else { 588 | // fw 2 had kCommandAutoScanInterval, which takes a WORD 589 | // representing cycles of the 32kHz clock 590 | // which was used to start the timer in one-shot mode. 591 | uint16_t arg = ms * 32 + 0.5f; 592 | i2c_char_t buf[] = { kCommandAutoScanInterval, (i2c_char_t)(arg >> 8), (i2c_char_t)(arg & 0xFF) }; 593 | if(WRITE_COMMAND_BUF(buf)) 594 | return 1; 595 | } 596 | return 0; 597 | } 598 | 599 | int Trill::setAutoScanInterval(uint16_t interval) 600 | { 601 | if(setTimerPeriod(interval / 32.f)) 602 | return 1; 603 | if(firmware_version_ >= 3) { 604 | // backwards compatibility: when using v3 library with v3 fw, 605 | // but the application was written for fw 2 606 | if(interval) { 607 | // ensure scanning on timer is enabled 608 | ScanTriggerMode mode = ScanTriggerMode(scanTriggerMode | kScanTriggerTimer); 609 | if(setScanTrigger(ScanTriggerMode(mode))) 610 | return 1; 611 | } 612 | } 613 | return 0; 614 | } 615 | 616 | int Trill::setScanTrigger(ScanTriggerMode mode) { 617 | REQUIRE_FW_AT_LEAST(3); 618 | scanTriggerMode = mode; 619 | i2c_char_t buf[] = { kCommandScanTrigger, i2c_char_t(scanTriggerMode) }; 620 | return WRITE_COMMAND_BUF(buf); 621 | } 622 | 623 | int Trill::setEventMode(EventMode mode) { 624 | REQUIRE_FW_AT_LEAST(3); 625 | i2c_char_t buf[] = { kCommandEventMode, i2c_char_t(mode) }; 626 | return WRITE_COMMAND_BUF(buf); 627 | } 628 | 629 | int Trill::setChannelMask(uint32_t mask) 630 | { 631 | REQUIRE_FW_AT_LEAST(3); 632 | i2c_char_t* bMask = (i2c_char_t*)&mask; 633 | i2c_char_t buf[] = { kCommandChannelMaskLow, bMask[0], bMask[1] }; 634 | if(WRITE_COMMAND_BUF(buf)) 635 | return 1; 636 | buf[0] = kCommandChannelMaskHigh; 637 | buf[1] = bMask[2]; 638 | buf[2] = bMask[3]; 639 | if(WRITE_COMMAND_BUF(buf)) 640 | return 1; 641 | updateChannelMask(mask); 642 | return 0; 643 | } 644 | 645 | int Trill::setTransmissionFormat(uint8_t width, uint8_t shift) 646 | { 647 | REQUIRE_FW_AT_LEAST(3); 648 | i2c_char_t buf[] = { kCommandFormat, width, shift }; 649 | if(WRITE_COMMAND_BUF(buf)) 650 | return 1; 651 | transmissionWidth = width; 652 | transmissionRightShift = shift; 653 | return 0; 654 | } 655 | 656 | int Trill::updateBaseline() { 657 | return WRITE_COMMAND(kCommandBaselineUpdate); 658 | } 659 | 660 | int Trill::reset() { 661 | REQUIRE_FW_AT_LEAST(3); 662 | return WRITE_COMMAND(kCommandReset); 663 | } 664 | 665 | static unsigned int bytesFromSlots(size_t numWords, size_t transmissionWidth) 666 | { 667 | switch(transmissionWidth) 668 | { 669 | default: 670 | case 16: 671 | return numWords * 2; 672 | case 12: 673 | return numWords + (numWords + 1) / 2; 674 | case 8: 675 | return numWords; 676 | } 677 | } 678 | unsigned int Trill::getBytesToRead(bool includesStatusByte) 679 | { 680 | size_t bytesToRead = kCentroidLengthDefault; 681 | if(CENTROID == mode_) { 682 | if(device_type_ == SQUARE || device_type_ == HEX) 683 | bytesToRead = kCentroidLength2D; 684 | if(device_type_ == RING) 685 | bytesToRead = kCentroidLengthRing; 686 | } else { 687 | bytesToRead = bytesFromSlots(getNumChannels(), transmissionWidth); 688 | } 689 | bytesToRead += sizeof(TrillStatusByte) * includesStatusByte; 690 | return bytesToRead; 691 | } 692 | 693 | int Trill::readI2C(bool shouldReadStatusByte) { 694 | if(NONE == device_type_ || readErrorOccurred) 695 | return 1; 696 | // NOTE: to avoid being too verbose, we do not check for firmware 697 | // version here. On fw < 3, shouldReadStatusByte will read one more 698 | // byte full of garbage. 699 | 700 | ssize_t bytesToRead = getBytesToRead(shouldReadStatusByte); 701 | dataBuffer.resize(bytesToRead); 702 | i2c_char_t offset = shouldReadStatusByte ? kOffsetStatusByte : kOffsetChannelData; 703 | if(READ_BYTES_FROM(offset, dataBuffer.data(), dataBuffer.size())) 704 | { 705 | num_touches_ = 0; 706 | fprintf(stderr, "Trill: error while reading from device %s at address %#x (%d)\n", 707 | getNameFromDevice(device_type_).c_str(), address, address); 708 | readErrorOccurred = true; 709 | return 1; 710 | } 711 | parseNewData(shouldReadStatusByte); 712 | return 0; 713 | } 714 | 715 | void Trill::newData(const uint8_t* newData, size_t len, bool includesStatusByte) 716 | { 717 | // we ensure dataBuffer's size is consistent with readI2C(), regardless 718 | // of how many bytes are actually passed here. 719 | dataBuffer.resize(getBytesToRead(includesStatusByte)); 720 | memcpy(dataBuffer.data(), newData, std::min(len * sizeof(newData[0]), sizeof(dataBuffer[0]) * dataBuffer.size())); 721 | parseNewData(includesStatusByte); 722 | } 723 | 724 | void Trill::parseNewData(bool includesStatusByte) 725 | { 726 | // by the time this is called, dataBuffer will have been resized appropriately 727 | uint8_t* src = this->dataBuffer.data(); 728 | size_t srcSize = this->dataBuffer.size(); 729 | if(!srcSize) 730 | return; 731 | if(includesStatusByte) 732 | { 733 | processStatusByte(src[0]); 734 | src++; 735 | srcSize--; 736 | } 737 | dataBufferIncludesStatusByte = includesStatusByte; 738 | if(CENTROID != mode_) { 739 | // parse, rescale and copy data to public buffer 740 | float rawRescale = this->rawRescale * (1 << transmissionRightShift); 741 | switch(transmissionWidth) 742 | { 743 | default: 744 | case 16: 745 | for (unsigned int i = 0; i < getNumChannels(); ++i) 746 | rawData[i] = ((src[2 * i] << 8) + src[2 * i + 1]) * rawRescale; 747 | break; 748 | case 12: 749 | { 750 | uint8_t* p = src; 751 | const uint8_t* end = src + srcSize; 752 | for (unsigned int i = 0; i < getNumChannels() && p < end; ++i) 753 | { 754 | uint16_t val; 755 | if(i & 1) { 756 | val = ((*p++) & 0xf0) << 4; 757 | val |= *p++; 758 | } else { 759 | val = *p++ << 4; 760 | val |= (*p & 0xf); 761 | } 762 | rawData[i] = val * rawRescale; 763 | } 764 | } 765 | break; 766 | case 8: 767 | for (unsigned int i = 0; i < getNumChannels(); ++i) 768 | rawData[i] = src[i] * rawRescale; 769 | break; 770 | } 771 | } else { 772 | unsigned int locations = 0; 773 | // Look for 1st instance of 0xFFFF (no touch) in the buffer 774 | for(locations = 0; locations < MAX_TOUCH_1D_OR_2D; locations++) 775 | { 776 | if(src[2 * locations] == 0xFF && src[2 * locations + 1] == 0xFF) 777 | break; 778 | } 779 | num_touches_ = locations; 780 | 781 | if(device_type_ == SQUARE || device_type_ == HEX) 782 | { 783 | // Look for the number of horizontal touches in 2D sliders 784 | // which might be different from number of vertical touches 785 | for(locations = 0; locations < MAX_TOUCH_1D_OR_2D; locations++) 786 | { 787 | if(src[2 * locations + 4 * MAX_TOUCH_1D_OR_2D] == 0xFF 788 | && src[2 * locations + 4 * MAX_TOUCH_1D_OR_2D+ 1] == 0xFF) 789 | break; 790 | } 791 | num_touches_ |= (locations << 4); 792 | } 793 | } 794 | } 795 | 796 | void Trill::processStatusByte(uint8_t newStatusByte) 797 | { 798 | statusByte = newStatusByte; 799 | uint8_t newFrameId = TrillStatusByte::parse(statusByte).frameId; 800 | if(newFrameId < (frameId & 0x3f)) 801 | frameId += 0x40; 802 | frameId = (frameId & 0xffffffc0) | (newFrameId); 803 | } 804 | 805 | int Trill::readStatusByte() 806 | { 807 | REQUIRE_FW_AT_LEAST(3); 808 | uint8_t newStatusByte; 809 | if(READ_BYTE_FROM(kOffsetStatusByte, newStatusByte)) 810 | return -1; 811 | processStatusByte(newStatusByte); 812 | return newStatusByte; 813 | } 814 | 815 | bool Trill::hasReset() 816 | { 817 | return !TrillStatusByte::parse(statusByte).initialised; 818 | } 819 | 820 | bool Trill::hasActivity() 821 | { 822 | return TrillStatusByte::parse(statusByte).activity; 823 | } 824 | 825 | uint8_t Trill::getFrameId() { 826 | return TrillStatusByte::parse(statusByte).frameId; 827 | } 828 | 829 | uint32_t Trill::getFrameIdUnwrapped() { 830 | return frameId; 831 | } 832 | 833 | bool Trill::is1D() 834 | { 835 | if(CENTROID != mode_) 836 | return false; 837 | switch(device_type_) { 838 | case BAR: 839 | case RING: 840 | case CRAFT: 841 | case FLEX: 842 | return true; 843 | default: 844 | return false; 845 | } 846 | } 847 | 848 | bool Trill::is2D() 849 | { 850 | if(CENTROID != mode_) 851 | return false; 852 | switch(device_type_) { 853 | case SQUARE: 854 | case HEX: 855 | return true; 856 | default: 857 | return false; 858 | } 859 | } 860 | 861 | unsigned int Trill::getNumTouches() 862 | { 863 | if(mode_ != CENTROID) 864 | return 0; 865 | 866 | // Lower 4 bits hold number of 1-axis or vertical touches 867 | return (num_touches_ & 0x0F); 868 | } 869 | 870 | unsigned int Trill::getNumHorizontalTouches() 871 | { 872 | if(mode_ != CENTROID || (device_type_ != SQUARE && device_type_ != HEX)) 873 | return 0; 874 | 875 | // Upper 4 bits hold number of horizontal touches 876 | return (num_touches_ >> 4); 877 | } 878 | #define dbOffset (dataBufferIncludesStatusByte * sizeof(TrillStatusByte)) 879 | 880 | float Trill::touchLocation(uint8_t touch_num) 881 | { 882 | if(mode_ != CENTROID) 883 | return -1; 884 | if(touch_num >= MAX_TOUCH_1D_OR_2D) 885 | return -1; 886 | 887 | int location = dataBuffer[dbOffset + 2 * touch_num] * 256; 888 | location += dataBuffer[dbOffset + 2 * touch_num + 1]; 889 | 890 | return location * posRescale; 891 | } 892 | 893 | float Trill::getButtonValue(uint8_t button_num) 894 | { 895 | if(mode_ != CENTROID) 896 | return -1; 897 | if(button_num > 1) 898 | return -1; 899 | if(device_type_ != RING) 900 | return -1; 901 | 902 | return (( 903 | (dataBuffer[dbOffset + 4 * MAX_TOUCH_1D_OR_2D + 2 * button_num] << 8) 904 | + dataBuffer[dbOffset + 4 * MAX_TOUCH_1D_OR_2D + 2 * button_num + 1] 905 | ) & 0x0FFF) * rawRescale; 906 | } 907 | 908 | float Trill::touchSize(uint8_t touch_num) 909 | { 910 | if(mode_ != CENTROID) 911 | return -1; 912 | if(touch_num >= MAX_TOUCH_1D_OR_2D) 913 | return -1; 914 | 915 | int size = dataBuffer[dbOffset + 2 * touch_num + 2 * MAX_TOUCH_1D_OR_2D] * 256; 916 | size += dataBuffer[dbOffset + 2 * touch_num + 2 * MAX_TOUCH_1D_OR_2D + 1]; 917 | 918 | return size * sizeRescale; 919 | } 920 | 921 | float Trill::touchHorizontalLocation(uint8_t touch_num) 922 | { 923 | if(mode_ != CENTROID || (device_type_ != SQUARE && device_type_ != HEX)) 924 | return -1; 925 | if(touch_num >= MAX_TOUCH_1D_OR_2D) 926 | return -1; 927 | 928 | int location = dataBuffer[dbOffset + 2 * touch_num + 4 * MAX_TOUCH_1D_OR_2D] * 256; 929 | location += dataBuffer[dbOffset + 2 * touch_num + 4 * MAX_TOUCH_1D_OR_2D+ 1]; 930 | 931 | return location * posHRescale; 932 | } 933 | 934 | float Trill::touchHorizontalSize(uint8_t touch_num) 935 | { 936 | if(mode_ != CENTROID || (device_type_ != SQUARE && device_type_ != HEX)) 937 | return -1; 938 | if(touch_num >= MAX_TOUCH_1D_OR_2D) 939 | return -1; 940 | 941 | int size = dataBuffer[dbOffset + 2 * touch_num + 6 * MAX_TOUCH_1D_OR_2D] * 256; 942 | size += dataBuffer[dbOffset + 2 * touch_num + 6* MAX_TOUCH_1D_OR_2D + 1]; 943 | 944 | return size * sizeRescale; 945 | } 946 | 947 | #define compoundTouch(LOCATION, SIZE, TOUCHES) {\ 948 | float avg = 0;\ 949 | float totalSize = 0;\ 950 | unsigned int numTouches = TOUCHES;\ 951 | for(unsigned int i = 0; i < numTouches; i++) {\ 952 | avg += LOCATION(i) * SIZE(i);\ 953 | totalSize += SIZE(i);\ 954 | }\ 955 | if(numTouches)\ 956 | avg = avg / totalSize;\ 957 | return avg;\ 958 | } 959 | 960 | float Trill::compoundTouchLocation() 961 | { 962 | compoundTouch(touchLocation, touchSize, getNumTouches()); 963 | } 964 | 965 | float Trill::compoundTouchHorizontalLocation() 966 | { 967 | compoundTouch(touchHorizontalLocation, touchHorizontalSize, getNumHorizontalTouches()); 968 | } 969 | 970 | float Trill::compoundTouchSize() 971 | { 972 | float size = 0; 973 | for(unsigned int i = 0; i < getNumTouches(); i++) 974 | size += touchSize(i); 975 | return size; 976 | } 977 | 978 | unsigned int Trill::getNumChannels() const 979 | { 980 | return numChannels; 981 | } 982 | 983 | unsigned int Trill::getDefaultNumChannels() const 984 | { 985 | switch(device_type_) { 986 | case BAR: return kNumChannelsBar; 987 | case RING: return kNumChannelsRing; 988 | default: return kNumChannelsMax; 989 | } 990 | } 991 | --------------------------------------------------------------------------------