├── docs ├── html.zip ├── Messung.xlsx ├── noisy_touch.png ├── normal_touch.png ├── Ausgleichsgerade.xlsx ├── noisy_touch_with_fir.png └── mainpage.dox ├── TFT_eFirFilter.h ├── README.md ├── library.properties ├── keywords.txt ├── examples ├── generic │ ├── edit_calibation │ │ ├── TMenu.h │ │ ├── MenuCounter.h │ │ ├── MenuCounter.cpp │ │ ├── edit_calibation.ino │ │ └── TMenu.cpp │ ├── raw │ │ └── raw.ino │ └── calibrate │ │ └── calibrate.ino └── TFT_eSPI │ └── Conways_Life │ └── Conways_Life.ino ├── TFT_eTouchGesture.h ├── TFT_eTouchGesture.cpp ├── TFT_eTouchBase.inl ├── TFT_eTouch.h ├── TFT_eTouchUser.h ├── TFT_eTouch.inl ├── TFT_eTouchBase.h └── TFT_eTouchBase.cpp /docs/html.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/achillhasler/TFT_eTouch/HEAD/docs/html.zip -------------------------------------------------------------------------------- /TFT_eFirFilter.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/achillhasler/TFT_eTouch/HEAD/TFT_eFirFilter.h -------------------------------------------------------------------------------- /docs/Messung.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/achillhasler/TFT_eTouch/HEAD/docs/Messung.xlsx -------------------------------------------------------------------------------- /docs/noisy_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/achillhasler/TFT_eTouch/HEAD/docs/noisy_touch.png -------------------------------------------------------------------------------- /docs/normal_touch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/achillhasler/TFT_eTouch/HEAD/docs/normal_touch.png -------------------------------------------------------------------------------- /docs/Ausgleichsgerade.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/achillhasler/TFT_eTouch/HEAD/docs/Ausgleichsgerade.xlsx -------------------------------------------------------------------------------- /docs/noisy_touch_with_fir.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/achillhasler/TFT_eTouch/HEAD/docs/noisy_touch_with_fir.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TFT_eTouch 2 | generic touch support for resistive chips 3 | 4 | unzip the Doxygen docu in docs for additional documentation 5 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=TFT_eTouch 2 | version=0.6.0 3 | author=Achill Hasler 4 | maintainer=Achill Hasler 5 | sentence=A library for restitive touchscreens for the Arduino IDE 6 | paragraph=Supports TFT displays that have a Adafruit compatible display driver. 7 | category=Display 8 | url=https://github.com/achillhasler/TFT_eTouch 9 | architectures=* 10 | includes=TFT_eTouch.h TFT_eTouchBase.h 11 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For TFT_eTouch 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | 9 | TFT_eTouch KEYWORD1 10 | TFT_eTouchBase KEYWORD1 11 | Calibation KEYWORD1 12 | Measure KEYWORD1 13 | TouchPoint KEYWORD1 14 | 15 | ####################################### 16 | # Methods and Functions (KEYWORD2) 17 | ####################################### 18 | 19 | init KEYWORD2 20 | getCalibration KEYWORD2 21 | getUserCalibration KEYWORD2 22 | setCalibration KEYWORD2 23 | readCalibration KEYWORD2 24 | writeCalibration KEYWORD2 25 | calibration KEYWORD2 26 | getXY KEYWORD2 27 | getRZ KEYWORD2 28 | getRaw KEYWORD2 29 | transform KEYWORD2 30 | setMeasure KEYWORD2 31 | setValidRawRange KEYWORD2 32 | setMeasureWait KEYWORD2 33 | getMeasureWait KEYWORD2 34 | setRXPlate KEYWORD2 35 | getRXPlate KEYWORD2 36 | setRZThreshold KEYWORD2 37 | getRZThreshold KEYWORD2 38 | setAcurateDistance KEYWORD2 39 | getAcurateDistance KEYWORD2 40 | -------------------------------------------------------------------------------- /examples/generic/edit_calibation/TMenu.h: -------------------------------------------------------------------------------- 1 | #ifndef T_MENU_H 2 | #define T_MENU_H 3 | 4 | // 5 | // TMenu.h 6 | // 7 | // (C) Copyright Achill Hasler 2019. 8 | // 9 | // Distributed under the Boost Software License, Version 1.0. 10 | // See accompanying file at https://www.boost.org/LICENSE_1_0.txt 11 | // 12 | 13 | #define TEST_RAW_INTERFACE 14 | 15 | #include 16 | #include "MenuCounter.h" 17 | 18 | class TMenu 19 | { 20 | TFT_eTouch& touch_; 21 | 22 | MenuCounter mc_[4]; 23 | uint8_t offset_; 24 | #ifdef TEST_RAW_INTERFACE 25 | TFT_eTouchBase::Measure raw_; 26 | TFT_eTouchBase::TouchPoint tp_; 27 | #else 28 | int16_t x_; 29 | int16_t y_; 30 | uint16_t rz_; 31 | #endif 32 | bool touched_; 33 | bool cursor_visible_; 34 | 35 | 36 | void draw(); 37 | void info(uint8_t offset); 38 | 39 | public: 40 | TMenu(TFT_eTouch& touch, uint8_t offset = 18); 41 | 42 | TFT_Driver& tft(); 43 | 44 | EventType pen_up(); 45 | EventType pen_down(); 46 | 47 | void init(); 48 | #ifdef TEST_RAW_INTERFACE 49 | void update(const TFT_eTouchBase::Measure& raw); 50 | #else 51 | void update(int16_t x, int16_t y, uint16_t rz); 52 | #endif 53 | void refresh(); 54 | }; 55 | 56 | #endif // T_MENU_H 57 | -------------------------------------------------------------------------------- /examples/generic/edit_calibation/MenuCounter.h: -------------------------------------------------------------------------------- 1 | #ifndef MENU_COUNTER_H 2 | #define MENU_COUNTER_H 3 | 4 | // 5 | // TMenuCounter.h 6 | // 7 | // (C) Copyright Achill Hasler 2019. 8 | // 9 | // Distributed under the Boost Software License, Version 1.0. 10 | // See accompanying file at https://www.boost.org/LICENSE_1_0.txt 11 | // 12 | 13 | #include 14 | 15 | typedef enum 16 | { 17 | none, 18 | changed, 19 | calibrate, 20 | store 21 | } EventType; 22 | 23 | class TMenu; 24 | 25 | class MenuCounter 26 | { 27 | TMenu* menu_; 28 | uint8_t fw_; 29 | int16_t x_p_; 30 | int16_t y_p_; 31 | bool x_grow_; 32 | 33 | char ch_; 34 | EventType ch_signal_; 35 | EventType signal_; 36 | 37 | uint16_t* val_; 38 | 39 | uint8_t cnt_step_; 40 | 41 | uint8_t activ_; 42 | uint8_t last_activ_; 43 | 44 | void draw_move(int16_t x_pos, int16_t y_pos, bool flag); 45 | 46 | public: 47 | MenuCounter(uint8_t fw = 20); 48 | 49 | void oner(TMenu* menu); 50 | TFT_Driver& tft() const; 51 | 52 | void pos(int16_t x_p, int16_t y_p, bool horizontal) 53 | { x_p_ = x_p; y_p_ = y_p; x_grow_ = horizontal; } 54 | 55 | void cmd(char ch, EventType event = none) 56 | { ch_ = ch; ch_signal_ = event; } 57 | 58 | void val(uint16_t& val) 59 | { val_ = &val; } 60 | 61 | void fw(uint8_t val) { fw_ = val; } 62 | uint8_t fw() const { return fw_; } 63 | 64 | EventType signal() { EventType ret = signal_; signal_ = none; return ret; } 65 | 66 | void pen_up(); 67 | 68 | void update(int16_t x, int16_t y, uint16_t rz); 69 | void draw(); 70 | }; 71 | 72 | #endif // MENU_COUNTER_H 73 | -------------------------------------------------------------------------------- /TFT_eTouchGesture.h: -------------------------------------------------------------------------------- 1 | #ifndef TFT_E_TOUCH_GESTURE_H 2 | #define TFT_E_TOUCH_GESTURE_H 3 | 4 | // 5 | // TFT_eTouchGesture.h 6 | // 7 | // (C) Copyright Achill Hasler 2019. 8 | // 9 | // Distributed under the Boost Software License, Version 1.0. 10 | // See accompanying file at https://www.boost.org/LICENSE_1_0.txt 11 | // 12 | // 13 | // See TFT_eTouch/docs/html/index.html for documentation. 14 | // 15 | 16 | #include 17 | 18 | /** 19 | * @brief Gesture support for touch 20 | */ 21 | class TFT_eTouchGesture 22 | { 23 | struct FilteredMeasure 24 | { 25 | uint8_t x; ///< raw x measure reduced to 8 bit (ca 213..3881)/16 26 | uint8_t y; ///< raw y measure reduced to 8 bit (ca 222..3929)/16 27 | uint8_t z1; ///< raw z1 measure reduced to 8 bit (ca 29..1748)/16 28 | uint8_t z2; ///< raw z2 measure reduced to 8 bit (ca 963..3989)/16 29 | uint8_t rz; ///< calculated resitor value < 0xff when tuched, 0xff when not tuched. 30 | uint16_t ms; 31 | FilteredMeasure() : rz(0xff) {} 32 | void operator=(const TFT_eTouchBase::Measure& raw); 33 | }; 34 | 35 | public: 36 | typedef enum 37 | { 38 | none, 39 | stay, 40 | move, 41 | wipe, 42 | zoom_in, 43 | zoom_out 44 | } Action; 45 | 46 | /** 47 | * @brief constructor 48 | * @param size stored values 49 | */ 50 | TFT_eTouchGesture(uint16_t size); 51 | ~TFT_eTouchGesture(); 52 | 53 | /** 54 | * You have to feed this instance with new measure in a constant timeframe. 55 | * @brief set measure 56 | * @param raw last readed touch measure 57 | */ 58 | void set(const TFT_eTouchBase::Measure& raw); 59 | 60 | /** 61 | * On pen up reset must call. 62 | * @brief reset 63 | */ 64 | void reset(); 65 | 66 | /** 67 | * Get the action. When Action move or wipe is returned the angle is valid. 68 | * @brief get action 69 | * @param angle in grad 70 | * @return actual action 71 | */ 72 | Action get(int16_t& angle); 73 | 74 | private: 75 | FilteredMeasure* data_; 76 | 77 | uint16_t size_; 78 | uint16_t next_; 79 | uint16_t used_; 80 | }; 81 | 82 | 83 | 84 | //#include 85 | 86 | #endif // TFT_E_TOUCH_GESTURE_H 87 | -------------------------------------------------------------------------------- /TFT_eTouchGesture.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // TFT_eTouchGesture.cpp 3 | // 4 | // (C) Copyright Achill Hasler 2019. 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // See accompanying file at https://www.boost.org/LICENSE_1_0.txt 8 | // 9 | // 10 | // See TFT_eTouch/docs/html/index.html for documentation. 11 | // 12 | 13 | #include 14 | 15 | void TFT_eTouchGesture::FilteredMeasure::operator=(const TFT_eTouchBase::Measure& raw) 16 | { 17 | x = raw.x >> 4; 18 | y = raw.y >> 4; 19 | z1 = raw.z1 >> 4; 20 | z2 = raw.z2 >> 4; 21 | if (raw.rz == 0xffff) rz = 255; 22 | else if (raw.rz > (254<<4)) rz = 254; 23 | else rz = raw.rz >> 4; 24 | ms = millis(); 25 | } 26 | 27 | TFT_eTouchGesture::TFT_eTouchGesture(uint16_t size) 28 | : data_(0) 29 | , size_(size) 30 | , next_(0) 31 | , used_(0) 32 | { 33 | data_ = new FilteredMeasure[size]; 34 | } 35 | 36 | TFT_eTouchGesture::~TFT_eTouchGesture() 37 | { 38 | delete data_; 39 | } 40 | 41 | void TFT_eTouchGesture::set(const TFT_eTouchBase::Measure& raw) 42 | { 43 | data_[next_++] = raw; 44 | if (next_ >= size_) next_ = 0; 45 | if (used_ < size_) used_++; 46 | } 47 | 48 | void TFT_eTouchGesture::reset() 49 | { 50 | next_ = 0; 51 | used_ = 0; 52 | } 53 | #define compare_ind(i1, i2) \ 54 | if (data_[i1].x < data_[i2].x) xneg=false; \ 55 | if (data_[i1].x > data_[i2].x) xpos=false; \ 56 | if (data_[i1].y < data_[i2].y) yneg=false; \ 57 | if (data_[i1].y > data_[i2].y) ypos=false 58 | 59 | TFT_eTouchGesture::Action TFT_eTouchGesture::get(int16_t& angle) 60 | { 61 | TFT_eTouchGesture::Action ret = none; 62 | if (used_ < size_) return ret; 63 | uint16_t ind = (size_ + next_ - 1) % size_; 64 | // int32_t dist = sqrt(pow((data_[ind].x - data_[next_].x), 2) + pow((data_[ind].y - data_[next_].y), 2)); 65 | // float dist = std::hypotf(data_[ind].x - data_[next_].x, data_[ind].y - data_[next_].y); 66 | float dist = ::hypotf(data_[ind].x - data_[next_].x, data_[ind].y - data_[next_].y); 67 | 68 | bool xneg=true, xpos=true, yneg=true, ypos=true; 69 | for (int i = next_; i < size_ - 1; i++) { 70 | compare_ind(i, i+1); 71 | } 72 | if (next_ != 0) { 73 | compare_ind(size_-1, 0); 74 | } 75 | for (int i = 0; i < next_ - 2; i++) { 76 | compare_ind(i, i+1); 77 | } 78 | return ret; 79 | } 80 | #undef compare_ind 81 | 82 | /* 83 | none, 84 | stay, 85 | move, 86 | wipe, 87 | zoom_in, 88 | zoom_out 89 | 90 | TFT_eTouchBase::Measure* data_; 91 | 92 | uint16_t size_; 93 | uint16_t next_; 94 | uint16_t used_; 95 | */ 96 | -------------------------------------------------------------------------------- /TFT_eTouchBase.inl: -------------------------------------------------------------------------------- 1 | #ifndef TFT_E_TOUCH_BASE_INL 2 | #define TFT_E_TOUCH_BASE_INL 3 | 4 | // 5 | // TFT_eTouchBase.inl 6 | // 7 | // (C) Copyright Achill Hasler 2019. 8 | // 9 | // Distributed under the Boost Software License, Version 1.0. 10 | // See accompanying file at https://www.boost.org/LICENSE_1_0.txt 11 | // 12 | // 13 | // See TFT_eTouch/docs/html/index.html for documentation. 14 | // 15 | 16 | // public inline members 17 | uint16_t TFT_eTouchBase::getRZ() const 18 | { 19 | return raw_.rz; 20 | } 21 | 22 | void TFT_eTouchBase::setCalibration(const Calibation& data) 23 | { 24 | calibation_ = data; 25 | } 26 | 27 | TFT_eTouchBase::Calibation& TFT_eTouchBase::calibration() 28 | { 29 | return calibation_; 30 | } 31 | 32 | void TFT_eTouchBase::setMeasure(uint8_t drop_first, bool z_once, bool z_first, bool z_local_min, uint8_t count) 33 | { 34 | drop_first_measures_ = drop_first; 35 | z_once_measure_ = z_once; 36 | z_first_measure_ = z_first; 37 | z_local_min_measure_ = z_local_min; 38 | 39 | count_measure_ = count; 40 | } 41 | 42 | #ifdef TOUCH_USE_AVERAGING_CODE 43 | void TFT_eTouchBase::setAveraging(bool averaging, bool ignore_min_max) 44 | { 45 | averaging_measure_ = averaging; 46 | ignore_min_max_measure_ = ignore_min_max; 47 | } 48 | #endif // end TOUCH_USE_AVERAGING_CODE 49 | 50 | void TFT_eTouchBase::setValidRawRange(uint16_t min, uint16_t max) 51 | { 52 | raw_valid_min_ = min; // raw measure minimum value from x, y, z1 and z2 (otherwise it is not touched) 53 | raw_valid_max_ = max; // raw measure maximum value 54 | } 55 | 56 | 57 | void TFT_eTouchBase::setMeasureWait(uint16_t ms) 58 | { 59 | measure_wait_ms_ = ms; 60 | } 61 | uint16_t TFT_eTouchBase::getMeasureWait() const 62 | { 63 | return measure_wait_ms_; 64 | } 65 | 66 | 67 | void TFT_eTouchBase::setRXPlate(uint16_t ohm) 68 | { 69 | rx_plate_ = ohm; 70 | } 71 | uint16_t TFT_eTouchBase::getRXPlate() const 72 | { 73 | return rx_plate_; 74 | } 75 | 76 | void TFT_eTouchBase::setRZThreshold(uint16_t ohm) 77 | { 78 | rz_threshold_ = ohm; 79 | } 80 | uint16_t TFT_eTouchBase::getRZThreshold() const 81 | { 82 | return rz_threshold_; 83 | } 84 | 85 | void TFT_eTouchBase::reset() 86 | { 87 | #ifdef TOUCH_FILTER_TYPE 88 | # ifdef TOUCH_X_FILTER 89 | x_filter_->reset(); 90 | # endif 91 | # ifdef TOUCH_Y_FILTER 92 | y_filter_->reset(); 93 | # endif 94 | # ifdef TOUCH_Z_FILTER 95 | z1_filter_->reset(); 96 | z2_filter_->reset(); 97 | # endif 98 | #endif 99 | } 100 | 101 | 102 | bool TFT_eTouchBase::valid() 103 | { 104 | return raw_.rz < rz_threshold_; 105 | } 106 | 107 | #ifdef TOUCH_USE_USER_CALIBRATION 108 | void TFT_eTouchBase::setAcurateDistance(uint16_t raw_difference) 109 | { 110 | acurate_difference_ = raw_difference; 111 | } 112 | uint16_t TFT_eTouchBase::getAcurateDistance() const 113 | { 114 | return acurate_difference_; 115 | } 116 | #endif // TOUCH_USE_USER_CALIBRATION 117 | 118 | // private inline members 119 | 120 | bool TFT_eTouchBase::is_touched() 121 | { 122 | return raw_.rz != 0xffff; 123 | } 124 | 125 | bool TFT_eTouchBase::in_range(uint16_t measure) 126 | { 127 | return (measure >= raw_valid_min_) && (measure <= raw_valid_max_); 128 | } 129 | 130 | void TFT_eTouchBase::spi_start() 131 | { 132 | spi_.beginTransaction(SPISettings(2000000, MSBFIRST, SPI_MODE0)); 133 | digitalWrite(cs_, LOW); 134 | } 135 | 136 | void TFT_eTouchBase::spi_end() 137 | { 138 | digitalWrite(cs_, HIGH); 139 | spi_.endTransaction(); 140 | } 141 | 142 | #endif // TFT_E_TOUCH_BASE_INL 143 | -------------------------------------------------------------------------------- /TFT_eTouch.h: -------------------------------------------------------------------------------- 1 | #ifndef TFT_E_TOUCH_H 2 | #define TFT_E_TOUCH_H 3 | 4 | // 5 | // TFT_eTouch.h 6 | // 7 | // (C) Copyright Achill Hasler 2019. 8 | // 9 | // Distributed under the Boost Software License, Version 1.0. 10 | // See accompanying file at https://www.boost.org/LICENSE_1_0.txt 11 | // 12 | // 13 | // See TFT_eTouch/docs/html/index.html for documentation. 14 | // 15 | 16 | #include 17 | 18 | /** 19 | * @brief touch support for tft 20 | * @param T used display driver, must be Adafuit compatible 21 | */ 22 | template 23 | class TFT_eTouch : public TFT_eTouchBase 24 | { 25 | public: 26 | /** 27 | * Create instance with defaults. 28 | * 29 | * Display and touch mostly use the same spi bus (SPI). This will by configured by the tft driver on tft.begin() and 30 | * the touch driver use this configuration. 31 | * When not you have to look that your used spi parameter get configured elsewhere bevor you call touch.begin(). 32 | * @brief constructor 33 | * @param tft used display, must be Adafuit compatible 34 | * @param cs_pin touch chip select line processor pin 35 | * @param penirq_pin touch penirq line processor pin. 0xff disable touch interrupt 36 | * @param spi used spi bus, configured by the tft driver 37 | */ 38 | TFT_eTouch(T& tft, uint8_t cs_pin, uint8_t penirq_pin = 0xff, SPIClass& spi = SPI); 39 | 40 | /** 41 | * Initialize the processor pins and initialize interrupt handling. 42 | * @brief init touch 43 | */ 44 | void init(); 45 | 46 | /** 47 | * Initialize the processor pins and initialize interrupt handling. 48 | * @brief begin touch 49 | */ 50 | void begin(); 51 | 52 | /** 53 | * Get Adafuit compatible display driver. 54 | * @brief get display 55 | * @return used display 56 | */ 57 | T& tft(); 58 | 59 | #ifdef TOUCH_USE_USER_CALIBRATION 60 | /** 61 | * Calculate the calibration values from 4 measures. User has to hit the blue target. 62 | * If it is get red try it again til it is green and next target is shown. 63 | * At the end a configuration string will be printed out to Serial. 64 | * @brief get calibration from user 65 | * @param data display coordinate of touch 66 | * @param xy_off display coordinate of touch 67 | * @return false on error otherwise data is set. 68 | */ 69 | bool getUserCalibration(Calibation& data, uint8_t xy_off); 70 | #endif 71 | 72 | /** 73 | * Tranfom a raw touch measure into display position. 74 | * The value tp is only set if the function returns true. 75 | * @brief raw to display 76 | * @param raw touch data 77 | * @param tp display data 78 | * @return true when raw data valid 79 | */ 80 | bool transform(const Measure& raw, TouchPoint& tp); 81 | 82 | /** 83 | * Get display position of touch. 84 | * The values x and y are only set if the function returns true. 85 | * @brief display position 86 | * @param x display coordinate of touch 87 | * @param y display coordinate of touch 88 | * @return true when display is tuched 89 | */ 90 | bool getXY(int16_t& x, int16_t& y); 91 | 92 | /** 93 | * Get display position and touched area of touch. 94 | * The value tp is only set if the function returns true. 95 | * @brief touch display values 96 | * @param tp touch 97 | * @return true when display is tuched 98 | */ 99 | bool get(TouchPoint& tp); 100 | 101 | #ifdef TOUCH_USE_PENIRQ_CODE 102 | # ifdef ESP32 103 | static void IRAM_ATTR cb_isr_touch_fnk(); 104 | # else 105 | static void cb_isr_touch_fnk(); 106 | # endif 107 | #endif // end TOUCH_USE_PENIRQ_CODE 108 | 109 | private: 110 | #ifdef TOUCH_USE_USER_CALIBRATION 111 | /** 112 | * Show one calibration target and get the raw touch values for that point. 113 | * The Target get red when not accurate and function returns false. 114 | * othewise the target get green and function returns true. 115 | * 116 | * If it is allways get red try a other configuration. see setAcurateDistance() 117 | * @brief one calibration target 118 | * @param point target, when the function returns true touch parameters are set. 119 | * @return true when accurate 120 | */ 121 | bool handleTouchCalibrationTarget(CalibrationPoint& point); 122 | #endif 123 | 124 | T& tft_; ///< the given display driver 125 | #ifdef TOUCH_USE_PENIRQ_CODE 126 | static TFT_eTouch* isr_instance_; ///< one instance used for interrupt handling 127 | #endif // end TOUCH_USE_PENIRQ_CODE 128 | }; 129 | 130 | #include 131 | 132 | #endif // TFT_E_TOUCH_H 133 | -------------------------------------------------------------------------------- /examples/generic/edit_calibation/MenuCounter.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // TMenuCounter.cpp 3 | // 4 | // (C) Copyright Achill Hasler 2019. 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // See accompanying file at https://www.boost.org/LICENSE_1_0.txt 8 | // 9 | 10 | #include "TMenu.h" 11 | 12 | void MenuCounter::draw_move(int16_t x_pos, int16_t y_pos, bool flag) 13 | { 14 | if (x_grow_) { 15 | const int16_t xm = x_pos + fw_*8/16; 16 | const int16_t xa = x_pos + fw_*4/16; 17 | const int16_t xe = x_pos + fw_*12/16; 18 | if (flag) { 19 | // up 20 | const int16_t ym = y_pos + fw_*4/16; 21 | tft().drawLine(xa, y_pos, xm, ym, TFT_WHITE); 22 | tft().drawLine(xe, y_pos, xm, ym, TFT_WHITE); 23 | } 24 | else { 25 | // down 26 | const int16_t ym = y_pos - fw_*4/16; 27 | tft().drawLine(xa, y_pos, xm, ym, TFT_WHITE); 28 | tft().drawLine(xe, y_pos, xm, ym, TFT_WHITE); 29 | } 30 | } 31 | else { 32 | const int16_t ym = y_pos + fw_*8/16; 33 | const int16_t ya = y_pos + fw_*4/16; 34 | const int16_t ye = y_pos + fw_*12/16; 35 | if (flag) { 36 | // right 37 | const int16_t xm = x_pos + fw_*4/16; 38 | tft().drawLine(x_pos, ya, xm, ym, TFT_WHITE); 39 | tft().drawLine(x_pos, ye, xm, ym, TFT_WHITE); 40 | } 41 | else { 42 | // left 43 | const int16_t xm = x_pos - fw_*4/16; 44 | tft().drawLine(x_pos, ya, xm, ym, TFT_WHITE); 45 | tft().drawLine(x_pos, ye, xm, ym, TFT_WHITE); 46 | } 47 | } 48 | } 49 | 50 | 51 | MenuCounter::MenuCounter(uint8_t fw) 52 | : menu_(0) 53 | , fw_(fw) 54 | , cnt_step_(12) 55 | , activ_(255) 56 | , last_activ_(255) 57 | , ch_signal_(none) 58 | , signal_(none) 59 | , val_(0) 60 | {} 61 | 62 | void MenuCounter::oner(TMenu* menu) 63 | { 64 | menu_ = menu; 65 | } 66 | 67 | TFT_Driver& MenuCounter::tft() const 68 | { 69 | return menu_->tft(); 70 | } 71 | 72 | void MenuCounter::pen_up() 73 | { 74 | activ_ = 255; 75 | if (last_activ_ < 5) draw(); 76 | } 77 | 78 | void MenuCounter::update(int16_t x, int16_t y, uint16_t rz) 79 | { 80 | static uint32_t last_update = 0; 81 | 82 | bool inside = false; 83 | if ((x > x_p_) && (y > y_p_)) { 84 | x -= x_p_; 85 | y -= y_p_; 86 | if (x_grow_) { 87 | if ((x < (5 * fw_)) && (y < (1 * fw_))) inside = true; 88 | } 89 | else { 90 | if ((x < (1 * fw_)) && (y < (5 * fw_))) inside = true; 91 | } 92 | } 93 | if (!inside) { 94 | activ_ = 255; 95 | if (signal_ != none) { 96 | signal_ = none; 97 | draw(); 98 | } 99 | return; 100 | } 101 | uint8_t ind; 102 | if (x_grow_) { 103 | ind = x / fw_; 104 | } 105 | else { 106 | ind = y / fw_; 107 | } 108 | if (last_update + 500 < millis()) { 109 | last_update = millis(); 110 | uint16_t val = *val_; 111 | // Serial.printf("update %d val: %p %d -> ", ind, val_, val); 112 | switch (ind) { 113 | case 0: // decrement var 114 | if (val - cnt_step_ > 200) val -= cnt_step_; 115 | break; 116 | case 1: 117 | if (val > 200) val--; 118 | break; 119 | case 2: 120 | signal_ = ch_signal_; 121 | break; 122 | case 3: 123 | if (val < 4000) val++; 124 | break; 125 | case 4: 126 | if (val + cnt_step_ < 4000) val += cnt_step_; 127 | break; 128 | default: 129 | return; 130 | } 131 | if (ind != 2) { 132 | signal_ = changed; 133 | *val_ = val; 134 | // Serial.println(val); 135 | } 136 | } 137 | activ_ = ind; 138 | } 139 | 140 | void MenuCounter::draw() 141 | { 142 | uint16_t i; 143 | 144 | if (activ_ == 2) tft().setTextColor(TFT_WHITE, TFT_BLUE); 145 | 146 | #ifdef BASIC_FONT_SUPPORT 147 | #define CH_X_OFF 8 148 | #define CH_Y_OFF 7 149 | #else 150 | #define CH_X_OFF 6 151 | #define CH_Y_OFF 3 152 | #endif 153 | 154 | if (x_grow_) { 155 | if (last_activ_ < 5 && activ_ != last_activ_) { 156 | tft().fillRect(x_p_ + 1 + last_activ_ * fw_, y_p_ + 1, fw_-1, fw_-1, TFT_BLACK); 157 | } 158 | for (i = 0; i <= 5; i++) { 159 | tft().drawFastVLine(x_p_ + i * fw_, y_p_ + 1, fw_ - 1, TFT_WHITE); 160 | } 161 | tft().drawFastHLine(x_p_, y_p_, 5 * fw_ + 1, TFT_WHITE); 162 | tft().drawFastHLine(x_p_, y_p_ + fw_, 5 * fw_ + 1, TFT_WHITE); 163 | if (activ_ < 5) { 164 | tft().fillRect(x_p_ + 1 + activ_ * fw_, y_p_ + 1, fw_-1, fw_-1, TFT_BLUE); 165 | } 166 | draw_move(x_p_, y_p_ + fw_*4/16, true); 167 | draw_move(x_p_, y_p_ + fw_*8/16, true); 168 | 169 | draw_move(x_p_ + fw_, y_p_ + fw_*6/16, true); 170 | 171 | tft().setCursor(x_p_ + 2*fw_ + CH_X_OFF, y_p_ + CH_Y_OFF); 172 | tft().print(ch_); 173 | 174 | draw_move(x_p_ + 3*fw_, y_p_ + fw_*10/16, false); 175 | 176 | draw_move(x_p_ + 4*fw_, y_p_ + fw_*8/16, false); 177 | draw_move(x_p_ + 4*fw_, y_p_ + fw_*12/16, false); 178 | } 179 | else { 180 | if (last_activ_ < 5 && activ_ != last_activ_) { 181 | tft().fillRect(x_p_ + 1, y_p_ + 1 + last_activ_ * fw_, fw_-1, fw_-1, TFT_BLACK); 182 | } 183 | for (i = 0; i <= 5; i++) { 184 | tft().drawFastHLine(x_p_ + 1, y_p_ + i * fw_, fw_ - 1, TFT_WHITE); 185 | } 186 | tft().drawFastVLine(x_p_, y_p_, 5 * fw_ + 1, TFT_WHITE); 187 | tft().drawFastVLine(x_p_ + fw_, y_p_, 5 * fw_ + 1, TFT_WHITE); 188 | if (activ_ < 5) { 189 | tft().fillRect(x_p_ + 1, y_p_ + 1 + activ_ * fw_, fw_-1, fw_-1, TFT_BLUE); 190 | } 191 | 192 | draw_move(x_p_ + fw_*8/16, y_p_, false); 193 | draw_move(x_p_ + fw_*12/16, y_p_, false); 194 | 195 | draw_move(x_p_ + fw_*10/16, y_p_ + fw_, false); 196 | 197 | tft().setCursor(x_p_ + CH_X_OFF, y_p_ + 2*fw_ + CH_Y_OFF); 198 | tft().print(ch_); 199 | 200 | draw_move(x_p_ + fw_*6/16, y_p_ + 3*fw_, true); 201 | 202 | draw_move(x_p_ + fw_*4/16, y_p_ + 4*fw_, true); 203 | draw_move(x_p_ + fw_*8/16, y_p_ + 4*fw_, true); 204 | } 205 | tft().setTextColor(TFT_WHITE, TFT_BLACK); 206 | 207 | last_activ_ = activ_; 208 | } 209 | -------------------------------------------------------------------------------- /docs/mainpage.dox: -------------------------------------------------------------------------------- 1 | /** 2 | @brief Documentation file for TFT_eTouch 3 | @author Achill Hasler 4 | @file mainpage.dox 5 | */ 6 | 7 | // we have to list all sample programs to show these in doxygen 8 | /** 9 | @example raw.ino Touch raw data example 10 | @example edit_calibation.ino Calibration example for TFT_eSPI driver 11 | @example calibrate.ino Calibration example for TFT_eSPI driver (obsolete use edit_calibation.ino) 12 | @example Conways_Life.ino Application example for TFT_eSPI driver (compare TFT_eTouch with integrated Touch in TFT_eSPI) 13 | */ 14 | 15 | /** 16 | @mainpage TFT_eTouch 17 | @section intro Intro 18 | Touchscreen 19 | - interface: spi & i2c & analog 20 | - type: resistive & capacitive 21 | 22 | This library supports resistive touchscreen chips with spi interface. 23 | 24 | @section config Config 25 | @subsection display Supported display 26 | The template parameter must be a Adafuit compatible display driver with followed interface 27 | @code 28 | class T { 29 | public: 30 | int16_t height(void) const; 31 | int16_t width(void) const; 32 | uint8_t getRotation(void) const; 33 | 34 | void fillScreen(uint32_t color); 35 | void drawPixel(uint32_t x, uint32_t y, uint32_t color); 36 | void drawCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color); 37 | void fillCircle(int32_t x0, int32_t y0, int32_t r, uint32_t color); 38 | void drawFastVLine(int32_t x, int32_t y, int32_t h, uint32_t color); 39 | void drawFastHLine(int32_t x, int32_t y, int32_t w, uint32_t color); 40 | 41 | void setCursor(int16_t x, int16_t y); 42 | void setTextSize(uint8_t size); 43 | void setTextColor(uint16_t fgcolor, uint16_t bgcolor); 44 | void print(..); // from Print 45 | void println(..); // from Print 46 | 47 | // when defined BASIC_FONT_SUPPORT follow function may be not avalible 48 | void setTextFont(uint8_t font); 49 | void drawString(..) 50 | }; 51 | @endcode 52 | 53 | @subsection chips Supported resistive chips 54 | - ADS7846 https://www.ti.com/lit/ds/symlink/ads7846.pdf 55 | - TSC2046 https://www.ti.com/lit/ds/symlink/tsc2046.pdf 56 | - XPT2064 https://www.buydisplay.com/download/ic/XPT2046.pdf 57 | - ADS7843 https://www.ti.com/lit/ds/symlink/ads7843.pdf 58 | - ADS7845 http://www.ti.com/lit/ds/symlink/ads7845.pdf 59 | - AD7873 https://www.analog.com/media/en/technical-documentation/data-sheets/ad7873.pdf 60 | - AD7843 https://www.analog.com/media/en/technical-documentation/data-sheets/ad7843.pdf 61 | 62 | @subsection raw Raw value definition 63 | All measured values (X, Y, Z1, Z2) has 12bit resolution, range is 0 .. 4095, bud valid range is 25 .. 4000. 64 | 65 | You can adjust the valid range with TFT_eTouchBase::setValidRawRange(). 66 | @subsection time Time for next fetch 67 | You can adjust the time between measures with TFT_eTouchBase::setMeasureWait(). As long the time is not reached, you get the same value from TFT_eTouchBase::getRaw(), TFT_eTouch::getXY() and TFT_eTouch::get(). 68 | 69 | @subsection algo Fetch algorithm 70 | There are two variants in sbaa036.pdf (Figure 10 and 11 page 8). You can wait til measure is same last measure (Figure 10) or taking the n conversation (Figure 11) or averaging n measures. 71 | When you averaging you can also ignore min and max measure. 72 | 73 | You can adjust the algorithm with TFT_eTouchBase::setMeasure() and TFT_eTouchBase::setAveraging(). 74 | 75 | If this is still noisy you can have a try with FIR filter. When using FIR there is a large delay. 76 | 77 | @subsection calibrate Calibrate 78 | There is a document from TI slyt277.pdf whitch describes calibration. In my callibration i use only offset and scaling correction no rotation correction. 79 | You can get a new calibation (TFT_eTouchBase::Calibation) with TFT_eTouch::getCalibration(). A existing calibration can be set with 80 | TFT_eTouchBase::setCalibration(). 81 | 82 | @section todo To Do 83 | @subsection read_write_cal calibation persistent 84 | We have to write a function to read/write the calibation from EPROM. (Atmel & Teensy missing) ESP32 & ESP8266 done. 85 | bool TFT_eTouchBase::readCalibration(), bool TFT_eTouchBase::writeCalibration(). 86 | 87 | @subsection analog analog Touch with X+, X-, Y+, Y- 88 | Support analog Touch. 89 | 90 | @subsection gesture gesture recognition 91 | Next will be gesture recognition. stay, wipe left, wipe right, wipe up and wipe down. (with one ore two fingers) 92 | If its possible zoom in / out. (only prepered) 93 | 94 | @section reference Reference 95 | @subsection pdf External documentation 96 | - http://www.ti.com/lit/an/sbaa036/sbaa036.pdf 97 | - http://www.ti.com/lit/an/slyt277/slyt277.pdf 98 | 99 | @subsection arduino Existing touch classes for Arduino 100 | - https://github.com/adafruit/Adafruit_STMPE610 101 | - https://github.com/PaulStoffregen/XPT2046_Touchscreen 102 | - https://github.com/adafruit/Touch-Screen-Library (analog Touch) 103 | 104 | using: 105 | - https://gist.github.com/CelliesProjects/99a56121e7c8a7cb00d5b46270e74b75 106 | 107 | @subsection linux Linux kernel sources 108 | - {linux_scr}/include/linux/spi/ads7846.h 109 | - {linux_scr}/drivers/drivers/input/touchscreen/ads7846.c 110 | 111 | - {linux_scr}/drivers/staging/fbtft 112 | 113 | @section history History 114 | - v0.4.0 First draft working with TFT_eSPI < 1.4.0 115 | - v0.5.0 tested with Tennsy 3.1, ESP32 added load save Configuration, added struct interface for raw & tft values, increasing speed when not touched. 116 | @subsection patch 1.4.0 < TFT_eSPI < 1.4.8 (latest TFT_eSPI lib has this patch) 117 | - add static getSPIinstance() function, for getting spi bus used by touchscreen. 118 | - when default config, (VSPI Port) do not create a new var, use the SPI var exported by SPI.h 119 | Header file TFT_eSPI.h 120 | @code 121 | void getSetup(setup_t& tft_settings); // Sketch provides the instance to populate 122 | +#ifndef ESP32_PARALLEL 123 | + static SPIClass& getSPIinstance(); 124 | +#endif 125 | 126 | int32_t cursor_x, cursor_y, padX; 127 | @endcode 128 | Sourcefile TFT_eSPI.cpp 129 | @code 130 | #if defined (ESP32) 131 | #if !defined (ESP32_PARALLEL) 132 | #ifdef USE_HSPI_PORT 133 | SPIClass spi(HSPI); 134 | #else // use default VSPI port 135 | +- SPIClass& spi = SPI; // use the predefined SPI variable which is defined by SPI.h 136 | #endif 137 | #endif 138 | #else // ESP8266 139 | +- SPIClass& spi = SPI; // use the predefined SPI variable which is defined by SPI.h 140 | #endif 141 | 142 | +#ifndef ESP32_PARALLEL 143 | +SPIClass& TFT_eSPI::getSPIinstance() { return spi; } 144 | +#endif 145 | 146 | @endcode 147 | 148 | - v0.6.0 tested with Tennsy 3.1, ESP32, ESP8266, Arduino NANO. 149 | - ESP8266 support 150 | - added FIR filter 151 | 152 | */ 153 | EOF 154 | -------------------------------------------------------------------------------- /TFT_eTouchUser.h: -------------------------------------------------------------------------------- 1 | #ifndef TFT_E_TOUCH_USER_H 2 | #define TFT_E_TOUCH_USER_H 3 | 4 | // 5 | // TFT_eTouchUser.h 6 | // 7 | // (C) Copyright Achill Hasler 2019. 8 | // 9 | // Distributed under the Boost Software License, Version 1.0. 10 | // See accompanying file at https://www.boost.org/LICENSE_1_0.txt 11 | // 12 | // 13 | // See TFT_eTouch/docs/html/index.html for documentation. 14 | // 15 | 16 | /** @def TFT_ETOUCH_CS 17 | * Define the chip select pin for the touch chip. 18 | */ 19 | #if defined (TEENSYDUINO) // with ILI9341 20 | # define TFT_ETOUCH_CS 15 21 | #elif defined (ESP8266) // with ILI9341 22 | # define TFT_ETOUCH_CS D1 // Chip select pin (T_CS) 23 | #elif 1 24 | # define TFT_ETOUCH_CS 3 25 | #else 26 | # define TFT_ETOUCH_CS SS 27 | #endif 28 | 29 | /** @def TFT_ETOUCH_PIRQ 30 | * Define the irq pin for the touch chip. 31 | */ 32 | #if 0 33 | # define TFT_ETOUCH_PIRQ 6 34 | #elif 0 //defined (ESP8266) // with ILI9341 35 | # define TFT_ETOUCH_PIRQ D0 // is touched signal pin (T_IRQ) is high when touched 36 | #elif 0 37 | # define TFT_ETOUCH_PIRQ 1 38 | #else 39 | # define TFT_ETOUCH_PIRQ 255 40 | #endif 41 | 42 | // include used TFT driver 43 | #if 0 // using Adafuit lib ILI9341 44 | #include 45 | typedef Adafruit_ILI9341 TFT_Driver; 46 | 47 | # define TFT_DC 4 48 | # define TFT_CS 3 49 | # define TFT_RST 17 // on SoftwareReset use -1, and connect pin to 3.3V 50 | 51 | #elif defined (TEENSYDUINO) // with ILI9341 52 | # include // Hardware-specific TFT library 53 | typedef ILI9341_t3 TFT_Driver; 54 | 55 | # define TFT_DC 20 // Data Command control pin 56 | # define TFT_CS 21 // Chip select pin 57 | # define TFT_RST 2 // on SoftwareReset use 255, and connect pin to 3.3V 58 | # define TFT_MOSI 7 // MasterOut SlaveIn pin (DOUT) 59 | # define TFT_SCLK 14 // SPI clock (SCK) 60 | # define TFT_MISO 12 // MasterIn SlaveOut pin (DIN) 61 | # define TFT_BL 3 // Backlight pin must have PWM for analogWrite if set 62 | 63 | #else // ESP_PLATFORM with eSPI 64 | #include // Hardware-specific TFT library 65 | typedef TFT_eSPI TFT_Driver; 66 | // manage your TFT setup in TFT_eSPI UserSetup.h 67 | 68 | # ifdef TOUCH_CS 69 | #error undef TOUCH_CS in TFT_eSPI UserSetup.h for using TFT_eTouch 70 | # endif 71 | 72 | #endif 73 | 74 | #ifndef TFT_ETOUCH_PIRQ 75 | # define TFT_ETOUCH_PIRQ 255 76 | #else 77 | # if (TFT_ETOUCH_PIRQ != 255) 78 | // if pin is set and valid we have to use penirq code 79 | # define TOUCH_USE_PENIRQ_CODE 80 | # endif 81 | #endif 82 | 83 | /** @def TOUCH_USE_PENIRQ_CODE 84 | * If this define is set the penrirq code is used. 85 | */ 86 | // undefine this to save progmem if you not have penirq or not using it 87 | #ifndef TOUCH_USE_PENIRQ_CODE 88 | //#define TOUCH_USE_PENIRQ_CODE 89 | #endif 90 | 91 | /** @def TOUCH_USE_AVERAGING_CODE 92 | * If this defined is set the averaging option is available. 93 | */ 94 | // undefine this to save progmem if you not using averaging 95 | #define TOUCH_USE_AVERAGING_CODE 96 | 97 | /** @def TOUCH_USE_USER_CALIBRATION 98 | * If this defined is set the member function getUserCalibration() is available. 99 | */ 100 | // undefine this to save progmem if you don't use getUserCalibration() anymore 101 | #define TOUCH_USE_USER_CALIBRATION 102 | 103 | /** @def TOUCH_USE_GESTURE 104 | * If this defined is set the gesture interface is available. (work in progress) 105 | */ 106 | // undefine this to save progmem if you don't use gesture interface anymore 107 | //#define TOUCH_USE_GESTURE 108 | 109 | /** @def TOUCH_USE_SIMPE_TARGET 110 | * If this defined is set the function getUserCalibration() show simple target. 111 | */ 112 | // define this to save progmem, simple target wont write outside dispay coordinates (undefine it looks nicer, bud can core on some dispay drivers) 113 | #define TOUCH_USE_SIMPE_TARGET 114 | 115 | /** @def TOUCH_USE_DIFFERENTIAL_MEASURE 116 | * If this defined is set the 'Differential Measure' mode is used. (SER/DFR low) 117 | * When undefined 'Single Ended Measure' mode is active. (SER/DFR high) 118 | */ 119 | #define TOUCH_USE_DIFFERENTIAL_MEASURE 120 | 121 | /** @def TOUCH_DEFAULT_CALIBRATION 122 | * This is the used touch configuration. If it's match to your configuration, you can disable TOUCH_USE_USER_CALIBRATION. 123 | */ 124 | #define TOUCH_DEFAULT_CALIBRATION { 272, 3749, 3894, 341, 0 } 125 | 126 | /** @def TOUCH_FILTER_TYPE 127 | * If this defined is set the touch driver filter raw data with a fir filter, 128 | * define additional TOUCH_X_FILTER, TOUCH_Y_FILTER, TOUCH_Z_FILTER for the value to filter 129 | * Window type: 130 | * - 1: Hamming 131 | * - 2: Hanning 132 | * - 3: Blackmann 133 | */ 134 | //#define TOUCH_FILTER_TYPE 1 135 | 136 | /** @def BASIC_FONT_SUPPORT 137 | * If this defined is set the tft driver lacks setTextFont() and drawString(). 138 | */ 139 | //#define BASIC_FONT_SUPPORT 140 | 141 | /** @def TOUCH_SERIAL_DEBUG 142 | * If this defined is set the library show info to Serial. 143 | */ 144 | // define this to see additional output on Serial 145 | #define TOUCH_SERIAL_DEBUG 146 | 147 | /** @def TOUCH_SERIAL_CONVERSATION_TIME 148 | * If this defined is set the library show time of one conversation in microsecond to Serial. 149 | * When TOUCH_SERIAL_DEBUG_FETCH is defined you got additional time from the print call! 150 | * When defined, getUserCalibration() won't work. 151 | */ 152 | // define this to see converation time on Serial for fetching raw values 153 | //#define TOUCH_SERIAL_CONVERSATION_TIME 154 | 155 | /** @def TOUCH_SERIAL_DEBUG_FETCH 156 | * If this defined is set the library show ctrl command of invalid read measure to Serial. 157 | */ 158 | // define this to see if X, Y, Z1 or Z2 measure is out of range when not touched 159 | //#define TOUCH_SERIAL_DEBUG_FETCH 160 | 161 | 162 | #ifdef TOUCH_FILTER_TYPE 163 | #include 164 | // undefine a filter or change N, N must be even (default 12), T must be uint16_t whitch is default 165 | #define TOUCH_X_FILTER FirFilter<20> 166 | #define TOUCH_Y_FILTER TOUCH_X_FILTER 167 | //#define TOUCH_Z_FILTER FirFilter<> 168 | #endif 169 | 170 | #ifndef TOUCH_DEFAULT_CALIBRATION 171 | #define TOUCH_DEFAULT_CALIBRATION { 300, 3700, 300, 3700, 2 } 172 | #endif 173 | 174 | #if defined (_ILI9341_t3H_) || defined (_ADAFRUIT_ILI9341H_) 175 | // color used by TFT_eTouch 176 | #define TFT_BLACK ILI9341_BLACK 177 | #define TFT_BLUE ILI9341_BLUE 178 | #define TFT_GREEN ILI9341_GREEN 179 | #define TFT_RED ILI9341_RED 180 | #define TFT_WHITE ILI9341_WHITE 181 | 182 | // missing setTextFont, drawString 183 | #ifndef BASIC_FONT_SUPPORT 184 | #define BASIC_FONT_SUPPORT 185 | #endif 186 | #endif 187 | 188 | 189 | #ifdef DOXYGEN 190 | // we set all for getting documentation 191 | #define TOUCH_USE_PENIRQ_CODE 192 | #define TOUCH_USE_AVERAGING_CODE 193 | #define TOUCH_USE_USER_CALIBRATION 194 | #define TOUCH_USE_SIMPE_TARGET 195 | #define TOUCH_USE_GESTURE 196 | #define TOUCH_USE_DIFFERENTIAL_MEASURE 197 | #define BASIC_FONT_SUPPORT 198 | #define TOUCH_SERIAL_DEBUG 199 | #define TOUCH_SERIAL_CONVERSATION_TIME 200 | #define TOUCH_SERIAL_DEBUG_FETCH 201 | #define TOUCH_FILTER_TYPE 1 202 | #endif 203 | 204 | #endif // TFT_E_TOUCH_USER_H 205 | -------------------------------------------------------------------------------- /examples/generic/raw/raw.ino: -------------------------------------------------------------------------------- 1 | /** 2 | Sketch to see raw touch values. 3 | 4 | The sketch has been tested with compatible ADS7846 chip on: 5 | - ESP32 (TFT_eSPI, Adafruit_ILI9341) 6 | - ESP8322 (TFT_eSPI) 7 | - Tensy 3.1 (ILI9341_t3) 8 | - Arduino Nano (Adafruit_ILI9341) 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | //------------------------------------------------------------------------------------------ 15 | 16 | #define TFT_ROTATION 1 17 | //#define MINMAX 18 | #define WITH_DISPLAY // some Displays make touch signal noisy 19 | //#define SECOND_SPI_PORT 20 | 21 | //------------------------------------------------------------------------------------------ 22 | #ifdef SECOND_SPI_PORT 23 | #define TFT_ETOUCH_SCK 14 24 | #define TFT_ETOUCH_MISO 12 25 | #define TFT_ETOUCH_MOSI 13 26 | 27 | SPIClass hSPI(HSPI); 28 | #endif 29 | //------------------------------------------------------------------------------------------ 30 | 31 | #ifdef WITH_DISPLAY 32 | # ifdef _ADAFRUIT_ILI9341H_ 33 | Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST); 34 | TFT_eTouch touch(tft, TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ); 35 | 36 | # elif defined (_ILI9341_t3H_) 37 | ILI9341_t3 tft(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO); 38 | TFT_eTouch touch(tft, TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ); 39 | 40 | # elif defined (_TFT_eSPIH_) 41 | TFT_eSPI tft; 42 | # ifdef SECOND_SPI_PORT 43 | TFT_eTouch touch(tft, TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ, hSPI); 44 | # else 45 | # ifdef ESP32_PARALLEL 46 | TFT_eTouch touch(tft, TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ, SPI); 47 | # else 48 | TFT_eTouch touch(tft, TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ, TFT_eSPI::getSPIinstance()); 49 | # endif 50 | # endif 51 | # else 52 | # error definition missing in TFT_eTouchUser.h 53 | # endif 54 | #else 55 | 56 | # ifdef SECOND_SPI_PORT 57 | TFT_eTouchBase touch(TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ, hSPI); 58 | # else 59 | TFT_eTouchBase touch(TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ, SPI); 60 | # endif 61 | 62 | #endif 63 | 64 | void setup() { 65 | Serial.begin(115200); 66 | delay(2000); 67 | 68 | #ifdef SECOND_SPI_PORT 69 | // hSPI.begin(); // ESP32 default SCK: 14, MISO: 12, MOSI: 13, SS :15. 70 | hSPI.begin(TFT_ETOUCH_SCK, TFT_ETOUCH_MISO, TFT_ETOUCH_MOSI, TFT_ETOUCH_CS); 71 | #endif 72 | #ifdef ESP32_PARALLEL 73 | SPI.begin(); 74 | #endif 75 | #ifdef WITH_DISPLAY 76 | tft.begin(); 77 | touch.init(); 78 | #else 79 | #ifdef TEENSYDUINO 80 | SPI.setMOSI(TFT_MOSI); 81 | SPI.setMISO(TFT_MISO); 82 | SPI.setSCK(TFT_SCLK); 83 | #else 84 | // raw test on ESP32 support only default SPI bus (MISO, MOSI & SCLK from pins_arduino.h) 85 | #endif 86 | touch.init(true); 87 | #endif 88 | while (!Serial) ; // wait for Arduino Serial Monitor 89 | // touch.setMeasure(0, false, true, false, 3); // constructor defaults (take third measure, start with z axis) 90 | // touch.setAveraging(false, false); // constructor defaults (without averaging) 91 | 92 | // touch.setMeasure(0, true, true, false, 1); // Differential mode fastest (each axis read only once, may work) 93 | // touch.setAcurateDistance(25); // in this mode acurate distance must be higher for getUserCalibration (default 10) 94 | 95 | // touch.setMeasure(1, true, true, false, 1); // Differential mode faster (take second z bud first x, y. may work) 96 | // touch.setAcurateDistance(25); // in this mode acurate distance must be higher for getUserCalibration (default 10) 97 | 98 | // touch.setMeasure(10, false, false, true, 14); // slowest (drop 10 and additional 16 measures. averaging 14 with min max value removed) 99 | // touch.setAveraging(true, true); 100 | 101 | // when noisy 102 | //touch.setMeasure(1, false, true, true, 3); // z first, drop 1 and additional 5 measures. averaging 3 with min max value removed 103 | //touch.setAveraging(true, true); 104 | //touch.setAcurateDistance(100); // or even higher for getUserCalibration (default 10) 105 | // or 106 | touch.setAcurateDistance(80); // and define TOUCH_FILTER_TYPE in TFT_eTouchUser.h 107 | 108 | // untouched: 35 us touched: 95 us (good choice for starting) 109 | touch.setMeasure(0, false, true, false, 2); // z first, take 2'th z,x,y 110 | /* if you get output like that when calling getUserCalibration, you have a noisy touch signal 111 | acurate on point[4, 4](233, 371) dx: 287 dy: 27 > 10 nok 112 | acurate on point[4, 4](283, 365) dx: 192 dy: 17 > 10 nok 113 | acurate on point[4, 4](349, 399) dx: 192 dy: 16 > 10 nok 114 | acurate on point[4, 4](334, 400) dx: 204 dy: 11 > 10 nok 115 | */ 116 | 117 | 118 | // touch.setMeasure(); // defaults (accurate all axis, start with x) 119 | // touch.setMeasure(1, true, true, true); // z with local min, acurate x,y 120 | 121 | // touch.setValidRawRange(10, 4085); 122 | // touch.setRXPlate(1000); 123 | // touch.setRZThreshold(1000*3); 124 | 125 | #ifdef WITH_DISPLAY 126 | tft.setRotation(TFT_ROTATION); 127 | delay(1); 128 | tft.fillScreen(TFT_BLACK); 129 | #ifdef BASIC_FONT_SUPPORT 130 | const char* str; 131 | str = "Touch raw test!"; 132 | int16_t len = strlen(str) * 6; 133 | tft.setCursor((tft.width() - len)/2, tft.height()/2); 134 | tft.print(str); 135 | #else 136 | tft.drawCentreString("Touch raw test!", tft.width()/2, tft.height()/2, 2); 137 | #endif 138 | #endif 139 | } 140 | 141 | void loop(void) { 142 | static uint32_t last_update = 0; 143 | if (last_update + touch.getMeasureWait() > millis()) return; 144 | last_update = millis(); 145 | uint16_t x = 0, y = 0, z1 = 0, z2 = 0, rz = 0; // To store the touch coordinates 146 | if (!touch.getRaw(x, y, z1, z2, rz)) return; 147 | 148 | #ifdef MINMAX 149 | static uint16_t count = 0; 150 | static uint16_t x_min = 0xffff, y_min = 0xffff, z1_min = 0xffff, z2_min = 0xffff, rz_min = 0xffff; 151 | static uint16_t x_max = 0, y_max = 0, z1_max = 0, z2_max = 0, rz_max = 0; // To store the touch coordinates 152 | if (x < x_min) x_min = x; if (x > x_max) x_max = x; 153 | if (y < y_min) y_min = y; if (y > y_max) y_max = y; 154 | if (z1 < z1_min) z1_min = z1; if (z1 > z1_max) z1_max = z1; 155 | if (z2 < z2_min) z2_min = z2; if (z2 > z2_max) z2_max = z2; 156 | if (rz < rz_min) rz_min = rz; if (rz > rz_max) rz_max = rz; 157 | if (count++ > 100) { 158 | count = 0; 159 | Serial.print("x (min, max),y (min, max),z1 (min, max),z2 (min, max),rz (min, max) = ("); 160 | Serial.print(x_min); 161 | Serial.print(","); 162 | Serial.print(x_max); 163 | Serial.print("),("); 164 | Serial.print(y_min); 165 | Serial.print(","); 166 | Serial.print(y_max); 167 | Serial.print("),("); 168 | Serial.print(z1_min); 169 | Serial.print(","); 170 | Serial.print(z1_max); 171 | Serial.print("),("); 172 | Serial.print(z2_min); 173 | Serial.print(","); 174 | Serial.print(z2_max); 175 | Serial.print("),("); 176 | Serial.print(rz_min); 177 | Serial.print(","); 178 | Serial.print(rz_max); 179 | Serial.println(")"); 180 | } 181 | #else 182 | Serial.print("x,y,z1,z2,rz = "); 183 | Serial.print(x); 184 | Serial.print(","); 185 | Serial.print(y); 186 | Serial.print(","); 187 | Serial.print(z1); 188 | Serial.print(","); 189 | Serial.print(z2); 190 | Serial.print(","); 191 | Serial.println(rz); 192 | #endif 193 | // delay(touch.getMeasureWait() + 1); // wait a little bit longer, for getting new measure values 194 | } 195 | -------------------------------------------------------------------------------- /examples/generic/edit_calibation/edit_calibation.ino: -------------------------------------------------------------------------------- 1 | /** 2 | Sketch to generate the setup() calibration values, these are reported 3 | to the Serial Monitor. 4 | 5 | The sketch has been tested on the ESP32 with compatible ADS7846 chip and TFT_eSPI driver. 6 | define TEST_RAW_INTERFACE in TMenu.h for raw test. 7 | */ 8 | 9 | #include 10 | #include 11 | 12 | #include "TMenu.h" 13 | 14 | //------------------------------------------------------------------------------------------ 15 | 16 | #define TFT_ROTATION 1 17 | 18 | //------------------------------------------------------------------------------------------ 19 | #ifdef _ADAFRUIT_ILI9341H_ 20 | Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST); 21 | TFT_eTouch touch(tft, TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ); 22 | 23 | #elif defined (_ILI9341_t3H_) 24 | ILI9341_t3 tft(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO); 25 | TFT_eTouch touch(tft, TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ); 26 | 27 | #elif defined (_TFT_eSPIH_) 28 | TFT_eSPI tft; 29 | TFT_eTouch touch(tft, TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ, TFT_eSPI::getSPIinstance()); 30 | 31 | #else 32 | # error definition missing in TFT_eTouchUser.h 33 | #endif 34 | 35 | TMenu menue(touch); 36 | 37 | //------------------------------------------------------------------------------------------ 38 | #define CALIBRATION_FILE "/TFT_eTouch.cal" 39 | 40 | void setup() { 41 | Serial.begin(115200); 42 | delay(2000); 43 | 44 | tft.begin(); 45 | touch.init(); 46 | while (!Serial) ; // wait for Arduino Serial Monitor 47 | 48 | #if 0 49 | // untouched: 35 us touched: 136 us 50 | touch.setMeasure(0, false, true, false, 3); // constructor defaults (take third measure, start with z axis) 51 | touch.setAveraging(false, false); // constructor defaults (without averaging) 52 | 53 | #elif 0 54 | // untouched: 35 us touched: 56 us 55 | touch.setMeasure(0, true, true, false, 1); // Differential mode fastest (each axis read only once, may work) 56 | #ifdef TOUCH_USE_USER_CALIBRATION 57 | touch.setAcurateDistance(25); // in this mode acurate distance must be higher for getUserCalibration (default 10) 58 | #endif 59 | 60 | #elif 0 61 | // untouched: 35 us touched: 77 us 62 | touch.setMeasure(1, true, true, false, 1); // Differential mode faster (take second z bud first x, y. may work) 63 | #ifdef TOUCH_USE_USER_CALIBRATION 64 | touch.setAcurateDistance(25); // in this mode acurate distance must be higher for getUserCalibration (default 10) 65 | #endif 66 | 67 | #elif 0 68 | // untouched: 35 us touched: d 190 max 600 us 69 | touch.setMeasure(1, true, true, true); // z with local min, acurate x,y, 35 us tot d 200 max 600 70 | 71 | #elif 0 72 | // untouched: 35 us touched: d 220 max 600 us 73 | touch.setMeasure(0, false, true); // z first, acurate z,x,y 74 | 75 | #elif 0 76 | // untouched: 35 us touched: d 190 max 600 us 77 | touch.setMeasure(2, true, true, false); // z first 3end, acurate x,y 78 | 79 | #elif 0 80 | // untouched: 35 us touched: 95 us 81 | touch.setMeasure(1, true, true, false, 2); // z first, take 2'th z,x,y 82 | 83 | #elif 0 84 | // untouched: 35 us touched: 95 us (good choice for starting, when not noisy) 85 | touch.setMeasure(0, false, true, false, 2); // z first, take 2'th z,x,y 86 | 87 | #elif 0 88 | // when noisy 89 | touch.setMeasure(0, false, true, true, 2); // z first, no drop and additional 4 measures. averaging 2 with min max value removed 90 | touch.setAveraging(true, true); 91 | #ifdef TOUCH_USE_USER_CALIBRATION 92 | touch.setAcurateDistance(100); // or even higher for getUserCalibration (default 10) 93 | #endif 94 | 95 | #elif 0 96 | // when noisy 97 | touch.setMeasure(1, false, true, true, 3); // z first, drop 1 and additional 5 measures. averaging 3 with min max value removed 98 | touch.setAveraging(true, true); 99 | #ifdef TOUCH_USE_USER_CALIBRATION 100 | touch.setAcurateDistance(100); // or even higher for getUserCalibration (default 10) 101 | #endif 102 | 103 | #elif 0 104 | // when noisy, untouched: 35 us touched: 334 us 105 | touch.setMeasure(3, false, true, false, 3); // drop 3 averaging 3 (+2 min max) 106 | touch.setAveraging(true, true); 107 | #ifdef TOUCH_USE_USER_CALIBRATION 108 | touch.setAcurateDistance(100); // or even higher for getUserCalibration (default 10) 109 | #endif 110 | 111 | #elif 0 112 | // when noisy, untouched: 35 us touched: 1302 us 113 | touch.setMeasure(10, false, true, true, 14); // slowest (drop 10(..255) and additional 16 measures. averaging 14 with min max value) 114 | touch.setAveraging(true, true); 115 | #ifdef TOUCH_USE_USER_CALIBRATION 116 | touch.setAcurateDistance(100); // or even higher for getUserCalibration (default 10) 117 | #endif 118 | 119 | #elif 1 120 | // when noisy, set a fast measure and define TOUCH_FILTER_TYPE in TFT_eTouchUser.h 121 | touch.setMeasure(0, false, true, false, 2); // z first, take 2'th z,x,y 122 | #ifdef TOUCH_USE_USER_CALIBRATION 123 | touch.setAcurateDistance(64); // in this mode acurate distance must be higher for getUserCalibration (default 10) 124 | #endif 125 | 126 | #else 127 | // default: untouched: 35 us touched: 136 us (take third measure, start with z axis) 128 | #endif 129 | 130 | touch.setRXPlate(1000); 131 | touch.setRZThreshold(1000*3); 132 | // touch.setMeasureWait(10); 133 | 134 | // SPIFFS.remove(CALIBRATION_FILE); 135 | if (!touch.readCalibration(CALIBRATION_FILE)) { 136 | #ifdef TOUCH_USE_USER_CALIBRATION 137 | TFT_eTouchBase::Calibation cal; 138 | touch.getUserCalibration(cal, 4); 139 | touch.setCalibration(cal); 140 | #else 141 | Serial.printf("Calibration not readed %s take default configuration %s\n", CALIBRATION_FILE, TOUCH_DEFAULT_CALIBRATION); 142 | #endif 143 | } 144 | 145 | tft.setRotation(TFT_ROTATION); 146 | menue.init(); // after rotation 147 | } 148 | 149 | //------------------------------------------------------------------------------------------ 150 | 151 | void loop(void) { 152 | static bool was_touched = false; 153 | static uint32_t last_touch = 0; 154 | 155 | if (last_touch + touch.getMeasureWait() > millis()) return; 156 | last_touch = millis(); 157 | 158 | #ifdef TEST_RAW_INTERFACE 159 | TFT_eTouchBase::Measure raw; 160 | if (touch.getRaw(raw)) { 161 | menue.update(raw); 162 | #else 163 | int16_t x, y; // screen coordinates 164 | if (touch.getXY(x, y)) { 165 | menue.update(x, y, touch.getRZ()); 166 | #endif 167 | if (!was_touched) { 168 | was_touched = true; 169 | Serial.println("pen down"); 170 | // we have pen down 171 | menue.pen_down(); 172 | } 173 | menue.refresh(); 174 | // Serial.println("pen activ"); 175 | } 176 | else { 177 | if (was_touched) { 178 | was_touched = false; 179 | Serial.print("pen up, "); 180 | // we have pen up 181 | switch (menue.pen_up()) { 182 | case changed: 183 | Serial.println("sig changed"); 184 | break; 185 | case calibrate: { 186 | #ifdef TOUCH_USE_USER_CALIBRATION 187 | Serial.println("sig calibrate"); 188 | TFT_eTouchBase::Calibation cal; 189 | touch.getUserCalibration(cal, 4); 190 | touch.setCalibration(cal); 191 | menue.init(); 192 | #else 193 | Serial.println("sig calibrate disabled"); 194 | #endif 195 | } 196 | break; 197 | case store: 198 | Serial.println("sig store"); 199 | touch.writeCalibration(CALIBRATION_FILE); 200 | break; 201 | case none: 202 | default: 203 | Serial.println("sig none"); 204 | break; 205 | } 206 | menue.refresh(); 207 | } 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /examples/generic/edit_calibation/TMenu.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // TMenu.cpp 3 | // 4 | // (C) Copyright Achill Hasler 2019. 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // See accompanying file at https://www.boost.org/LICENSE_1_0.txt 8 | // 9 | 10 | #include "TMenu.h" 11 | 12 | TMenu::TMenu(TFT_eTouch& touch, uint8_t offset) 13 | : touch_(touch) 14 | , offset_(offset) 15 | #ifndef TEST_RAW_INTERFACE 16 | , x_(0) 17 | , y_(0) 18 | , rz_(0) 19 | #endif 20 | , touched_(false) 21 | , cursor_visible_(false) 22 | { 23 | } 24 | 25 | TFT_Driver& TMenu::tft() 26 | { 27 | return touch_.tft(); 28 | } 29 | 30 | EventType TMenu::pen_up() 31 | { 32 | EventType ret = none; 33 | touched_ = false; 34 | touch_.reset(); 35 | for (uint8_t i = 0; i < 4 ; i++) { 36 | ret = mc_[i].signal(); 37 | mc_[i].pen_up(); 38 | if (ret > changed) break; 39 | } 40 | #ifdef TEST_RAW_INTERFACE 41 | tp_.rz++; 42 | #else 43 | rz_++; // change a littlte bit, for hideing cursor in next refresh call 44 | #endif 45 | return ret; 46 | } 47 | 48 | EventType TMenu::pen_down() 49 | { 50 | touched_ = true; 51 | return none; 52 | } 53 | 54 | void TMenu::init() 55 | { 56 | TFT_eTouchBase::Calibation& calibation = touch_.calibration(); 57 | uint8_t d_rot = (4 + tft().getRotation() - calibation.rel_rotation) % 4; 58 | 59 | // Serial.printf("d_rot %d\n", d_rot); 60 | 61 | for (uint8_t i = 0; i < 4 ; i++) { 62 | mc_[i].oner(this); 63 | } 64 | // pos from d_rot == 0 65 | mc_[0].pos(offset_, (tft().height() - 5 * mc_[0].fw()) / 2, false); 66 | mc_[1].pos((tft().width() - 5 * mc_[1].fw()) / 2, offset_, true); 67 | mc_[2].pos(tft().width() - 1 - offset_ - mc_[2].fw(), (tft().height() - 5 * mc_[2].fw()) / 2, false); 68 | mc_[3].pos((tft().width() - 5 * mc_[3].fw())/2, tft().height() - 1 - offset_ - mc_[3].fw(), true); 69 | 70 | mc_[(4 + 0 - d_rot) % 4].cmd('X', none); // do nothing 71 | mc_[(4 + 0 - d_rot) % 4].val(calibation.x0); 72 | 73 | mc_[(4 + 1 - d_rot) % 4].cmd('Y', none); // do nothing 74 | mc_[(4 + 1 - d_rot) % 4].val(calibation.y0); 75 | 76 | mc_[(4 + 2 - d_rot) % 4].cmd('S', store); // store calibation on pen up 77 | mc_[(4 + 2 - d_rot) % 4].val(calibation.x1); 78 | 79 | mc_[(4 + 3 - d_rot) % 4].cmd('C', calibrate); // calibrate on pen up 80 | mc_[(4 + 3 - d_rot) % 4].val(calibation.y1); 81 | 82 | // Clear the screen 83 | tft().fillScreen(TFT_BLACK); 84 | 85 | #ifdef BASIC_FONT_SUPPORT 86 | tft().setTextSize(1); 87 | char* str; 88 | str = "Touch screen!"; 89 | int16_t len = strlen(str) * 6; 90 | tft().setCursor((tft().width() - len)/2, tft().height()/2 - 20); 91 | tft().print(str); 92 | 93 | str = "C: calibrate"; 94 | len = strlen(str) * 6; 95 | tft().setCursor((tft().width()-len)/2 , tft().height()/2 - 0); 96 | tft().print(str); 97 | 98 | str = "S: store"; 99 | len = strlen(str) * 6; 100 | tft().setCursor((tft().width()-len)/2 , tft().height()/2 + 20); 101 | tft().print(str); 102 | 103 | str = "(on pen up)"; 104 | len = strlen(str) * 6; 105 | tft().setCursor((tft().width()-len)/2 , tft().height()/2 + 40); 106 | tft().print(str); 107 | #else 108 | tft().setTextFont(2); 109 | tft().setTextDatum(TC_DATUM); 110 | tft().drawString("Touch screen!", tft().width()/2, tft().height()/2 - 20, 2); 111 | tft().drawString("C: calibrate", tft().width()/2, tft().height()/2 - 0, 2); 112 | tft().drawString("S: store", tft().width()/2, tft().height()/2 + 20, 2); 113 | tft().drawString("(on pen up)", tft().width()/2, tft().height()/2 + 40, 2); 114 | tft().setTextDatum(TL_DATUM); 115 | #endif 116 | 117 | refresh(); 118 | } 119 | 120 | void TMenu::draw() 121 | { 122 | for (uint8_t i = 0; i < 4 ; i++) { 123 | mc_[i].draw(); 124 | } 125 | } 126 | 127 | void TMenu::info(uint8_t offset) 128 | { 129 | #ifdef TEST_RAW_INTERFACE 130 | tft().setCursor(offset, offset); 131 | tft().printf("Pos: %d, %d, %d ", tp_.x, tp_.y, tp_.rz); 132 | 133 | if (tp_.rz > touch_.getRXPlate()*174/100) tft().println("Pen "); 134 | else if (tp_.rz >= touch_.getRXPlate()*66/100) tft().println("Finger "); 135 | else if (tp_.rz > touch_.getRXPlate()*15/100) tft().println("2 Finger "); 136 | else tft().println("+ 2 Finger "); 137 | 138 | tft().setCursor(offset, tft().getCursorY()); 139 | tft().printf("Raw: %d, %d, %d,%d \n", raw_.x, raw_.y, raw_.z1, raw_.z2); 140 | #else 141 | uint16_t xr; 142 | uint16_t yr; 143 | uint16_t z1; 144 | uint16_t z2; 145 | uint16_t rz; 146 | touch_.getRaw(xr, yr, z1, z2, rz); 147 | 148 | tft().setCursor(offset, offset); 149 | tft().printf("Pos: %d, %d, %d ", x_, y_, rz_); 150 | if (rz_ > touch_.getRZThreshold()*58/100) tft().println("Pen "); 151 | else if (rz_ >= touch_.getRZThreshold()*22/100) tft().println("Finger "); 152 | else if (rz_ > touch_.getRZThreshold()*5/100) tft().println("2 Finger "); 153 | else tft().println("+ 2 Finger "); 154 | 155 | tft().setCursor(offset, tft().getCursorY()); 156 | tft().printf("Raw: %d, %d, %d,%d (%s) \n", xr, yr, z1, z2, (rz == rz_) ? "same" : "new" ); 157 | 158 | #endif 159 | tft().setCursor(offset, tft().getCursorY()); 160 | TFT_eTouchBase::Calibation& calibation = touch_.calibration(); 161 | tft().printf("Cal: %d,%d, %d,%d, %d \n", calibation.x0, calibation.x1, calibation.y0, calibation.y1, calibation.rel_rotation); 162 | } 163 | 164 | #ifdef TEST_RAW_INTERFACE 165 | void TMenu::update(const TFT_eTouchBase::Measure& raw) 166 | { 167 | if (touch_.transform(raw, tp_)) { 168 | raw_ = raw; 169 | for (uint8_t i = 0; i < 4 ; i++) { 170 | mc_[i].update(tp_.x, tp_.y, tp_.rz); 171 | } 172 | } 173 | } 174 | #else 175 | void TMenu::update(int16_t x, int16_t y, uint16_t rz) 176 | { 177 | x_ = x; 178 | y_ = y; 179 | rz_ = rz; 180 | 181 | for (uint8_t i = 0; i < 4 ; i++) { 182 | mc_[i].update(x, y, rz); 183 | } 184 | } 185 | #endif 186 | 187 | 188 | void TMenu::refresh() 189 | { 190 | const uint8_t cursor_size = 8, cursor_hole = 3; 191 | bool same_pos = false; 192 | 193 | #ifdef TEST_RAW_INTERFACE 194 | static TFT_eTouchBase::TouchPoint last; 195 | 196 | if (last.x == tp_.x && last.y == tp_.y) { 197 | if (last.rz == tp_.rz) return; 198 | same_pos = true; 199 | } 200 | 201 | if (cursor_visible_ && (!same_pos || !touched_)) { 202 | tft().drawFastHLine(last.x - cursor_size + 1, last.y, cursor_size - cursor_hole, TFT_BLACK); 203 | tft().drawFastHLine(last.x + cursor_hole, last.y, cursor_size - cursor_hole, TFT_BLACK); 204 | tft().drawFastVLine(last.x, last.y - cursor_size + 1, cursor_size - cursor_hole, TFT_BLACK); 205 | tft().drawFastVLine(last.x, last.y + cursor_hole, cursor_size - cursor_hole, TFT_BLACK); 206 | tft().drawPixel(last.x, last.y, TFT_BLACK); 207 | } 208 | 209 | draw(); 210 | if (!touched_) { 211 | cursor_visible_ = false; // hide cursor 212 | return; 213 | } 214 | 215 | info(50); 216 | 217 | if (!same_pos) { 218 | tft().drawFastHLine(tp_.x - cursor_size + 1, tp_.y, cursor_size - cursor_hole, TFT_WHITE); 219 | tft().drawFastHLine(tp_.x + cursor_hole, tp_.y, cursor_size - cursor_hole, TFT_WHITE); 220 | tft().drawFastVLine(tp_.x, tp_.y - cursor_size + 1, cursor_size - cursor_hole, TFT_WHITE); 221 | tft().drawFastVLine(tp_.x, tp_.y + cursor_hole, cursor_size - cursor_hole, TFT_WHITE); 222 | tft().drawPixel(tp_.x, tp_.y, TFT_WHITE); 223 | } 224 | last = tp_; 225 | #else 226 | static int16_t vx = 10000, vy = 10000, vz = 10000; 227 | 228 | if (vx == x_ && vy == y_) { 229 | if (vz == rz_) return; 230 | same_pos = true; 231 | } 232 | if (cursor_visible_ && (!same_pos || !touched_)) { 233 | tft().drawFastHLine(vx - cursor_size + 1, vy, cursor_size - cursor_hole, TFT_BLACK); 234 | tft().drawFastHLine(vx + cursor_hole, vy, cursor_size - cursor_hole, TFT_BLACK); 235 | tft().drawFastVLine(vx, vy - cursor_size + 1, cursor_size - cursor_hole, TFT_BLACK); 236 | tft().drawFastVLine(vx, vy + cursor_hole, cursor_size - cursor_hole, TFT_BLACK); 237 | tft().drawPixel(vx, vy, TFT_BLACK); 238 | } 239 | 240 | draw(); 241 | if (!touched_) { 242 | cursor_visible_ = false; // hide cursor 243 | return; 244 | } 245 | 246 | info(50); 247 | 248 | if (!same_pos) { 249 | tft().drawFastHLine(x_ - cursor_size + 1, y_, cursor_size - cursor_hole, TFT_WHITE); 250 | tft().drawFastHLine(x_ + cursor_hole, y_, cursor_size - cursor_hole, TFT_WHITE); 251 | tft().drawFastVLine(x_, y_ - cursor_size + 1, cursor_size - cursor_hole, TFT_WHITE); 252 | tft().drawFastVLine(x_, y_ + cursor_hole, cursor_size - cursor_hole, TFT_WHITE); 253 | tft().drawPixel(x_, y_, TFT_WHITE); 254 | } 255 | vx = x_; vy = y_; vz = rz_; 256 | #endif 257 | cursor_visible_ = true; 258 | } 259 | -------------------------------------------------------------------------------- /examples/generic/calibrate/calibrate.ino: -------------------------------------------------------------------------------- 1 | /** 2 | Sketch to generate the setup() calibration values, these are reported 3 | to the Serial Monitor. 4 | 5 | The sketch has been tested with compatible ADS7846 chip on: 6 | - ESP32 (TFT_eSPI, Adafruit_ILI9341) 7 | - ESP8322 (TFT_eSPI) 8 | - Tensy 3.1 (ILI9341_t3) 9 | - Arduino Nano (Adafruit_ILI9341) 10 | */ 11 | 12 | #include 13 | #include 14 | 15 | //------------------------------------------------------------------------------------------ 16 | 17 | #define TFT_ROTATION 1 18 | 19 | #define DEFAULT_CALIBRATION 20 | #define MAX_CALIBRATION 1 /* 0 .. 4 set it to 0 when callData ok */ 21 | 22 | //------------------------------------------------------------------------------------------ 23 | 24 | # ifdef _ADAFRUIT_ILI9341H_ 25 | Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST); 26 | TFT_eTouch touch(tft, TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ); 27 | 28 | # elif defined (_ILI9341_t3H_) 29 | ILI9341_t3 tft(TFT_CS, TFT_DC, TFT_RST, TFT_MOSI, TFT_SCLK, TFT_MISO); 30 | TFT_eTouch touch(tft, TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ); 31 | 32 | # elif defined (_TFT_eSPIH_) 33 | TFT_eSPI tft; 34 | # ifdef ESP32_PARALLEL 35 | TFT_eTouch touch(tft, TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ, SPI); 36 | # else 37 | TFT_eTouch touch(tft, TFT_ETOUCH_CS, TFT_ETOUCH_PIRQ, TFT_eSPI::getSPIinstance()); 38 | # endif 39 | # else 40 | # error definition missing in TFT_eTouchUser.h 41 | # endif 42 | 43 | 44 | void start_screen() 45 | { 46 | // Clear the screen 47 | tft.fillScreen(TFT_BLACK); 48 | 49 | #ifdef BASIC_FONT_SUPPORT 50 | tft.setTextSize(1); 51 | const char* str; 52 | str = "Touch screen!"; 53 | int16_t len = strlen(str) * 6; 54 | tft.setCursor((tft.width() - len)/2, tft.height()/2 - 20); 55 | tft.print(str); 56 | 57 | str = "R: rotate, C: calibrate"; 58 | len = strlen(str) * 6; 59 | tft.setCursor((tft.width()-len)/2 , tft.height()/2); 60 | tft.print(str); 61 | 62 | str = "open serial monitor to see calibration"; 63 | len = strlen(str) * 6; 64 | tft.setCursor((tft.width()-len)/2 , tft.height()/2 + 20); 65 | tft.print(str); 66 | #else 67 | tft.drawCentreString("Touch screen!", tft.width()/2, tft.height()/2 - 20, 2); 68 | tft.drawCentreString("R: rotate, C: calibrate", tft.width()/2, tft.height()/2, 2); 69 | tft.drawCentreString("open serial monitor to see calibration", tft.width()/2, tft.height()/2 + 20, 2); 70 | #endif 71 | tft.setCursor(0, 0); 72 | 73 | #ifdef BASIC_FONT_SUPPORT 74 | tft.setTextSize(2); 75 | #define CHAR_XSIZE_PIX (2 * 6) 76 | #else 77 | tft.setTextFont(2); 78 | tft.setTextSize(1); 79 | #define CHAR_XSIZE_PIX 10 80 | #endif 81 | tft.setTextColor(TFT_WHITE, TFT_BLUE); 82 | tft.print("R"); 83 | tft.setCursor(tft.width() - CHAR_XSIZE_PIX, 0); 84 | tft.print("C"); 85 | tft.setTextColor(TFT_WHITE, TFT_BLACK); 86 | #ifdef BASIC_FONT_SUPPORT 87 | tft.setTextSize(1); 88 | #endif 89 | } 90 | 91 | void show_cursor(int16_t x, int16_t y) 92 | { 93 | // this is not a real cursor, it does not store the background under the cursor 94 | static bool visible = false; 95 | static int16_t vx = 10000, vy = 10000, vz = 10000; 96 | 97 | const uint8_t size = 8, hole = 3; 98 | uint16_t rz = touch.getRZ(); 99 | bool same_pos = false; 100 | if (vx == x && vy == y) { 101 | if (vz == rz) return; 102 | same_pos = true; 103 | } 104 | if (visible && !same_pos) { 105 | tft.drawFastHLine(vx - size + 1, vy, size - hole, TFT_BLACK); 106 | tft.drawFastHLine(vx + hole, vy, size - hole, TFT_BLACK); 107 | tft.drawFastVLine(vx, vy - size + 1, size - hole, TFT_BLACK); 108 | tft.drawFastVLine(vx, vy + hole, size - hole, TFT_BLACK); 109 | tft.drawPixel(vx, vy, TFT_BLACK); 110 | } 111 | if (x < 0 || y < 0) { 112 | visible = false; // coordinate invalid, hide cursor 113 | return; 114 | } 115 | tft.setCursor(50, 50); 116 | tft.print(x); 117 | tft.print(","); 118 | tft.print(y); 119 | tft.print(","); 120 | tft.print(rz); 121 | tft.println(" "); 122 | 123 | if (rz > touch.getRXPlate()*174/100) tft.println("Pen "); 124 | else if (rz > touch.getRXPlate()*66/100) tft.println("Finger "); 125 | else if (rz > touch.getRXPlate()*15/100) tft.println("2 Finger "); 126 | else tft.println("+ 2 Finger "); 127 | /* 128 | if (rz > touch.getRZThreshold()*58/100) tft.println("Pen "); 129 | else if (rz >= touch.getRZThreshold()*22/100) tft.println("Finger "); 130 | else if (rz > touch.getRZThreshold()*5/100) tft.println("2 Finger "); 131 | else tft.println("more then 2 Finger"); 132 | */ 133 | if (!same_pos) { 134 | tft.drawFastHLine(x - size + 1, y, size - hole, TFT_WHITE); 135 | tft.drawFastHLine(x + hole, y, size - hole, TFT_WHITE); 136 | tft.drawFastVLine(x, y - size + 1, size - hole, TFT_WHITE); 137 | tft.drawFastVLine(x, y + hole, size - hole, TFT_WHITE); 138 | tft.drawPixel(x, y, TFT_WHITE); 139 | } 140 | vx = x; vy = y; vz = rz; 141 | visible = true; 142 | } 143 | 144 | //------------------------------------------------------------------------------------------ 145 | 146 | void setup() { 147 | Serial.begin(115200); 148 | delay(2000); 149 | 150 | #ifdef ESP32_PARALLEL 151 | SPI.begin(); 152 | #endif 153 | tft.begin(); 154 | touch.init(); 155 | while (!Serial) ; // wait for Arduino Serial Monitor 156 | // touch.setMeasure(0, false, true, false, 3); // constructor defaults (take third measure, start with z axis) 157 | // touch.setAveraging(false, false); // constructor defaults (without averaging) 158 | 159 | // touch.setMeasure(0, true, true, false, 1); // Differential mode fastest (each axis read only once, may work) 160 | // touch.setAcurateDistance(25); // in this mode acurate distance must be higher for getUserCalibration (default 10) 161 | 162 | // touch.setMeasure(1, true, true, false, 1); // Differential mode faster (take second z bud first x, y. may work) 163 | // touch.setAcurateDistance(25); // in this mode acurate distance must be higher for getUserCalibration (default 10) 164 | 165 | // touch.setMeasure(10, false, false, true, 14); // slowest (drop 10 and additional 16 measures. averaging 14 with min max value removed) 166 | // touch.setAveraging(true, true); 167 | 168 | // when noisy 169 | //touch.setMeasure(1, false, true, true, 3); // z first, drop 1 and additional 5 measures. averaging 3 with min max value removed 170 | //touch.setAveraging(true, true); 171 | //touch.setAcurateDistance(100); // or even higher for getUserCalibration (default 10) 172 | // or 173 | touch.setAcurateDistance(80); // and define TOUCH_FILTER_TYPE in TFT_eTouchUser.h 174 | 175 | // untouched: 35 us touched: 95 us (good choice for starting) 176 | touch.setMeasure(0, false, true, false, 2); // z first, take 2'th z,x,y 177 | /* if you get output like that when calling getUserCalibration, you have a noisy touch signal 178 | acurate on point[4, 4](233, 371) dx: 287 dy: 27 > 10 nok 179 | acurate on point[4, 4](283, 365) dx: 192 dy: 17 > 10 nok 180 | acurate on point[4, 4](349, 399) dx: 192 dy: 16 > 10 nok 181 | acurate on point[4, 4](334, 400) dx: 204 dy: 11 > 10 nok 182 | */ 183 | 184 | 185 | // touch.setMeasure(); // defaults (accurate all axis, start with x) 186 | // touch.setMeasure(1, true, true, true); // z with local min, acurate x,y 187 | 188 | // touch.setValidRawRange(10, 4085); 189 | 190 | touch.setRXPlate(1000/3); 191 | touch.setRZThreshold(1000); 192 | 193 | #ifndef DEFAULT_CALIBRATION 194 | TFT_eTouchBase::Calibation calibation = { 265, 3790, 264, 3850, 2 }; // x and y axes have same direction on touch & display 195 | // TFT_eTouchBase::Calibation calibation = { 3790, 265, 3850, 264, 0 }; // same as above 196 | // TFT_eTouchBase::Calibation calibation = { 272, 3749, 3894, 341, 0 }; // y axes have oposite direction 197 | 198 | // Calibrate the touch screen and retrieve the scaling factors 199 | for (int rot = 0; rot < MAX_CALIBRATION; rot++) { 200 | // Set the rotation before we calibrate 201 | tft.setRotation(TFT_ROTATION + rot % 4); 202 | touch.getUserCalibration(calibation, 4); 203 | } 204 | touch.setCalibration(calibation); 205 | #endif 206 | tft.setRotation(TFT_ROTATION); 207 | delay(1); 208 | start_screen(); 209 | } 210 | 211 | //------------------------------------------------------------------------------------------ 212 | 213 | void loop(void) { 214 | static uint32_t last_update = 0; 215 | if (last_update + touch.getMeasureWait() > millis()) return; 216 | last_update = millis(); 217 | 218 | static bool is_touched = false; 219 | int16_t x, y; // screen coordinates 220 | if (touch.getXY(x, y)) { 221 | if (!is_touched) { // we have pen down! 222 | is_touched = true; 223 | } 224 | if (y < 10) { 225 | if (x < 10) { 226 | show_cursor(-1, -1); // hide cursor 227 | uint8_t rot = tft.getRotation(); 228 | rot = (rot + 1) % 4; 229 | tft.setRotation(rot); 230 | start_screen(); 231 | return; 232 | } 233 | if (x > tft.width() - 10) { 234 | TFT_eTouchBase::Calibation calibation; 235 | show_cursor(-1, -1); // hide cursor 236 | touch.getUserCalibration(calibation, 4); 237 | touch.setCalibration(calibation); 238 | start_screen(); 239 | return; 240 | } 241 | } 242 | show_cursor(x, y); 243 | } 244 | else { 245 | if (is_touched) { // we have pen up! 246 | is_touched = false; 247 | show_cursor(-1, -1); // hide cursor 248 | touch.reset(); 249 | } 250 | } 251 | 252 | } 253 | -------------------------------------------------------------------------------- /TFT_eTouch.inl: -------------------------------------------------------------------------------- 1 | #ifndef TFT_E_TOUCH_INL 2 | #define TFT_E_TOUCH_INL 3 | 4 | // 5 | // TFT_eTouch.inl 6 | // 7 | // (C) Copyright Achill Hasler 2019. 8 | // 9 | // Distributed under the Boost Software License, Version 1.0. 10 | // See accompanying file at https://www.boost.org/LICENSE_1_0.txt 11 | // 12 | // 13 | // See TFT_eTouch/docs/html/index.html for documentation. 14 | // 15 | 16 | #ifdef TOUCH_USE_PENIRQ_CODE 17 | template 18 | TFT_eTouch* TFT_eTouch::isr_instance_ = 0; 19 | 20 | template 21 | #ifdef ESP32 22 | void IRAM_ATTR TFT_eTouch::cb_isr_touch_fnk() 23 | #else 24 | void TFT_eTouch::cb_isr_touch_fnk() 25 | #endif 26 | { 27 | TFT_eTouch* touch_ptr = isr_instance_; 28 | if (touch_ptr) touch_ptr->update_allowed_ = true; 29 | } 30 | #endif // end TOUCH_USE_PENIRQ_CODE 31 | 32 | 33 | template 34 | TFT_eTouch::TFT_eTouch(T& tft, uint8_t cs_pin, uint8_t penirq_pin, SPIClass& spi) 35 | : TFT_eTouchBase(cs_pin, penirq_pin, spi) 36 | , tft_(tft) 37 | { 38 | } 39 | 40 | template 41 | void TFT_eTouch::begin() 42 | { 43 | init(); 44 | } 45 | 46 | template 47 | void TFT_eTouch::init() 48 | { 49 | TFT_eTouchBase::init(false); 50 | #ifdef TOUCH_USE_PENIRQ_CODE 51 | if (penirq_ != 0xff) { 52 | pinMode(penirq_, INPUT); 53 | attachInterrupt(digitalPinToInterrupt(penirq_), cb_isr_touch_fnk, FALLING); 54 | isr_instance_ = this; 55 | } 56 | #endif // end TOUCH_USE_PENIRQ_CODE 57 | } 58 | 59 | template 60 | T& TFT_eTouch::tft() 61 | { 62 | return tft_; 63 | } 64 | 65 | #ifdef TOUCH_USE_USER_CALIBRATION 66 | template 67 | bool TFT_eTouch::getUserCalibration(Calibation& data, uint8_t xy_off) 68 | { 69 | uint8_t user_r = tft_.getRotation(); 70 | uint8_t touch_r = 255; 71 | 72 | tft_.fillScreen(TFT_BLACK); 73 | tft_.setCursor(50, 50); 74 | #ifndef BASIC_FONT_SUPPORT 75 | tft_.setTextFont(2); 76 | #endif 77 | tft_.setTextSize(1); 78 | tft_.setTextColor(TFT_WHITE, TFT_BLACK); 79 | 80 | tft_.println("Touch blue target to green"); 81 | 82 | #ifndef BASIC_FONT_SUPPORT 83 | tft_.setTextFont(1); 84 | #endif 85 | tft_.println(); 86 | 87 | if (xy_off < 4) xy_off = 4; 88 | 89 | CalibrationPoint cal_pos[4]; // get clockwise 4 values 90 | cal_pos[0].set(xy_off, xy_off); // Top Left 91 | cal_pos[1].set(tft_.width() - 1 - xy_off, xy_off); // Top Right 92 | cal_pos[2].set(tft_.width() - 1 - xy_off, tft_.height() - 1 - xy_off); // Down Right 93 | cal_pos[3].set(xy_off, tft_.height() - 1 - xy_off); // Down Left 94 | for (int i = 0; i < 4; i++) { 95 | uint8_t try_cnt = 4; 96 | reset(); // clear FIR filter when used 97 | while (try_cnt > 0 && !handleTouchCalibrationTarget(cal_pos[i])) { 98 | reset(); 99 | delay(500); 100 | try_cnt--; 101 | } 102 | } 103 | 104 | #ifdef TOUCH_SERIAL_DEBUG 105 | if (Serial) { 106 | Serial.print("p0 "); cal_pos[0].print(); 107 | Serial.print("p1 "); cal_pos[1].print(); 108 | Serial.print("p2 "); cal_pos[2].print(); 109 | Serial.print("p3 "); cal_pos[3].print(); 110 | } 111 | #endif 112 | 113 | for (int i = 0; i < 4; i++) { 114 | if (cal_pos[i].touch_x < 1000 && cal_pos[i].touch_y < 1000) { 115 | // zerro of touch, is touch_y inverted ? 116 | if (cal_pos[(i+4-1) % 4].touch_x < 1000) { 117 | // not inverted 118 | touch_r = i; 119 | } 120 | else { 121 | touch_r = (i + 1) % 4; 122 | } 123 | break; 124 | } 125 | } 126 | 127 | #ifdef TOUCH_SERIAL_DEBUG 128 | if (Serial) { 129 | Serial.print("Rotation: tft "); 130 | Serial.print(user_r); 131 | Serial.print(" touch "); 132 | Serial.println(touch_r); 133 | } 134 | #endif 135 | 136 | int16_t d_xs = cal_pos[1].scr_x - cal_pos[0].scr_x; 137 | int16_t d_xt = (cal_pos[(touch_r + 1) % 4].touch_x + cal_pos[(touch_r + 2) % 4].touch_x)/2 - (cal_pos[touch_r + 0].touch_x + cal_pos[(touch_r + 3) % 4].touch_x)/2; 138 | 139 | #ifdef TOUCH_SERIAL_DEBUG 140 | if (Serial) { 141 | Serial.print("dx "); 142 | Serial.print(d_xs); 143 | Serial.print(", "); 144 | Serial.print(d_xt); 145 | } 146 | #endif 147 | 148 | int16_t x0 = cal_pos[0].scr_x - d_xs * (cal_pos[touch_r + 0].touch_x+cal_pos[(touch_r + 3) % 4].touch_x) / (d_xt * 2); 149 | #ifdef TOUCH_SERIAL_DEBUG 150 | if (Serial) { 151 | Serial.print(", "); 152 | Serial.println(x0); 153 | } 154 | #endif 155 | 156 | data.x0 = -x0 * d_xt / d_xs; 157 | data.x1 = (tft_.width() - 1 - x0) * d_xt / d_xs; 158 | 159 | int16_t d_ys = cal_pos[3].scr_y - cal_pos[0].scr_y; 160 | int16_t d_yt = (cal_pos[(touch_r + 2) % 4].touch_y + cal_pos[(touch_r + 3) % 4].touch_y)/2 - (cal_pos[touch_r + 0].touch_y + cal_pos[(touch_r + 1) % 4].touch_y)/2; 161 | 162 | #ifdef TOUCH_SERIAL_DEBUG 163 | if (Serial) { 164 | Serial.print("dy "); 165 | Serial.print(d_ys); 166 | Serial.print(", "); 167 | Serial.print(d_yt); 168 | } 169 | #endif 170 | 171 | int16_t y0 = cal_pos[0].scr_y - d_ys * (cal_pos[touch_r + 0].touch_y+cal_pos[(touch_r + 1) % 4].touch_y) / (d_yt * 2); 172 | 173 | #ifdef TOUCH_SERIAL_DEBUG 174 | if (Serial) { 175 | Serial.print(", "); 176 | Serial.println(y0); 177 | } 178 | #endif 179 | 180 | data.y0 = -y0 * d_yt / d_ys; 181 | data.y1 = (tft_.height() - 1 - y0) * d_yt / d_ys; 182 | 183 | data.rel_rotation = (user_r + touch_r) % 4; 184 | 185 | #ifdef TOUCH_SERIAL_DEBUG 186 | if (Serial) { 187 | Serial.print("x: "); Serial.print(data.x0); Serial.print(" "); Serial.print(data.x1); 188 | Serial.print(" y: "); Serial.print(data.y0); Serial.print(" "); Serial.print(data.y1); 189 | Serial.print(" rot "); Serial.println(data.rel_rotation); 190 | } 191 | #endif 192 | 193 | if (Serial) { 194 | Serial.printf("#define TOUCH_DEFAULT_CALIBRATION { %i, %i, %i, %i, %i }\n", 195 | data.x0, data.x1, data.y0, data.y1, data.rel_rotation); 196 | } 197 | 198 | return true; 199 | } 200 | /* 201 | acurate on p[4, 4] is 100, dx 292, dy 8 202 | acurate on p[4, 4] is 100, dx 268, dy 12 203 | acurate on p[4, 4] is 100, dx 273, dy 11 204 | acurate on p[4, 4] is 100, dx 294, dy 5 205 | acurate on p[4, 4] is 100, dx 38, dy 16 206 | acurate on p[315, 4] is 100, dx 43, dy 81 207 | acurate on p[315, 235] is 100, dx 152, dy 113 208 | acurate on p[315, 235] is 100, dx 189, dy 129 209 | acurate on p[315, 235] is 100, dx 93, dy 68 210 | acurate on p[4, 235] is 100, dx 54, dy 7 211 | #define TOUCH_DEFAULT_CALIBRATION { 263, 3765, 3873, 326, 0 } 212 | */ 213 | 214 | #endif // TOUCH_USE_USER_CALIBRATION 215 | 216 | template 217 | bool TFT_eTouch::transform(const Measure& raw, TouchPoint& tp) 218 | { 219 | if (raw.rz != 0xffff) { 220 | uint8_t d_rot = (4 + tft_.getRotation() - calibation_.rel_rotation) % 4; 221 | 222 | int16_t dx = calibation_.x1 - calibation_.x0; 223 | int16_t dy = calibation_.y1 - calibation_.y0; 224 | 225 | int16_t xs=0, xs_max = tft_.width() - 1; 226 | int16_t ys=0, ys_max = tft_.height() - 1; 227 | 228 | switch (d_rot) { 229 | case 0: 230 | xs = (raw.x - calibation_.x0) * xs_max / dx; 231 | ys = (raw.y - calibation_.y0) * ys_max / dy; 232 | break; 233 | case 1: 234 | xs = (raw.y - calibation_.y0) * xs_max / dy; 235 | ys = ys_max - ((raw.x - calibation_.x0) * ys_max / dx); 236 | break; 237 | case 2: 238 | xs = xs_max - ((raw.x - calibation_.x0) * xs_max / dx); 239 | ys = ys_max - ((raw.y - calibation_.y0) * ys_max / dy); 240 | break; 241 | case 3: 242 | xs = xs_max - ((raw.y - calibation_.y0) * xs_max / dy); 243 | ys = (raw.x - calibation_.x0) * ys_max / dx; 244 | break; 245 | } 246 | if (xs < 0) xs = 0; 247 | if (xs > xs_max) xs = xs_max; 248 | 249 | if (ys < 0) ys = 0; 250 | if (ys > ys_max) ys = ys_max; 251 | 252 | tp.set(xs, ys, raw.rz); 253 | return true; 254 | } 255 | tp.rz = 0xffff; 256 | return false; 257 | } 258 | 259 | template 260 | bool TFT_eTouch::getXY(int16_t& x, int16_t& y) 261 | { 262 | update(false); 263 | if (valid()) { 264 | TouchPoint tp; 265 | transform(raw_, tp); 266 | x = tp.x; 267 | y = tp.y; 268 | 269 | return true; 270 | } 271 | return false; 272 | } 273 | 274 | template 275 | bool TFT_eTouch::get(TouchPoint& tp) 276 | { 277 | update(false); 278 | if (valid()) { 279 | return transform(raw_, tp); 280 | } 281 | return false; 282 | } 283 | 284 | // private 285 | #ifdef TOUCH_USE_USER_CALIBRATION 286 | 287 | #ifdef TOUCH_USE_SIMPE_TARGET 288 | template 289 | bool TFT_eTouch::handleTouchCalibrationTarget(CalibrationPoint& point) 290 | { 291 | tft_.fillRect(point.scr_x - 4, point.scr_y - 4, 9, 9, TFT_BLUE); 292 | tft_.fillCircle(point.scr_x, point.scr_y, 4, TFT_WHITE); 293 | tft_.fillCircle(point.scr_x, point.scr_y, 2, TFT_BLUE); 294 | 295 | bool acurate = acurateCalibrationTarget(point); 296 | 297 | tft_.fillRect(point.scr_x - 4, point.scr_y - 4, 9, 9, acurate ? TFT_GREEN : TFT_RED); 298 | tft_.fillCircle(point.scr_x, point.scr_y, 4, TFT_WHITE); 299 | tft_.fillCircle(point.scr_x, point.scr_y, 2, acurate ? TFT_GREEN : TFT_RED); 300 | 301 | // wait pen up 302 | waitPenUp(); 303 | delay(500); 304 | 305 | return acurate; 306 | } 307 | #else // !TOUCH_USE_SIMPE_TARGET 308 | template 309 | bool TFT_eTouch::handleTouchCalibrationTarget(CalibrationPoint& point) 310 | { 311 | const uint8_t size = 10, hole = 3; 312 | 313 | tft_.fillCircle(point.scr_x, point.scr_y, 10, TFT_WHITE); 314 | tft_.fillCircle(point.scr_x, point.scr_y, 9, TFT_BLUE); 315 | tft_.fillCircle(point.scr_x, point.scr_y, 4, TFT_WHITE); 316 | tft_.fillCircle(point.scr_x, point.scr_y, 2, TFT_BLACK); 317 | if (point.scr_x < size || point.scr_y < size || point.scr_x > tft_.width() - size || point.scr_y > tft_.height() - size) { 318 | // fillCircle won't work if not full visible 319 | tft_.drawCircle(point.scr_x, point.scr_y, 3, TFT_WHITE); 320 | tft_.drawCircle(point.scr_x, point.scr_y, 4, TFT_WHITE); 321 | tft_.drawCircle(point.scr_x, point.scr_y, 5, TFT_BLUE); 322 | tft_.drawCircle(point.scr_x, point.scr_y, 6, TFT_BLUE); 323 | tft_.drawCircle(point.scr_x, point.scr_y, 7, TFT_BLUE); 324 | tft_.drawCircle(point.scr_x, point.scr_y, 8, TFT_BLUE); 325 | tft_.drawCircle(point.scr_x, point.scr_y, 9, TFT_BLUE); 326 | tft_.drawCircle(point.scr_x, point.scr_y, 10, TFT_WHITE); 327 | } 328 | tft_.drawFastHLine(point.scr_x - size + 1, point.scr_y, size - hole, TFT_WHITE); 329 | tft_.drawFastHLine(point.scr_x + hole, point.scr_y, size - hole, TFT_WHITE); 330 | tft_.drawFastVLine(point.scr_x, point.scr_y - size + 1, size - hole, TFT_WHITE); 331 | tft_.drawFastVLine(point.scr_x, point.scr_y + hole, size - hole, TFT_WHITE); 332 | 333 | bool acurate = acurateCalibrationTarget(point); 334 | 335 | tft_.fillCircle(point.scr_x, point.scr_y, 9, acurate ? TFT_GREEN : TFT_RED); 336 | tft_.fillCircle(point.scr_x, point.scr_y, 4, TFT_WHITE); 337 | tft_.fillCircle(point.scr_x, point.scr_y, 2, TFT_BLACK); 338 | if (point.scr_x < size || point.scr_y < size || point.scr_x > tft_.width() - size || point.scr_y > tft_.height() - size) { 339 | tft_.drawCircle(point.scr_x, point.scr_y, 5, acurate ? TFT_GREEN : TFT_RED); 340 | tft_.drawCircle(point.scr_x, point.scr_y, 6, acurate ? TFT_GREEN : TFT_RED); 341 | tft_.drawCircle(point.scr_x, point.scr_y, 7, acurate ? TFT_GREEN : TFT_RED); 342 | tft_.drawCircle(point.scr_x, point.scr_y, 8, acurate ? TFT_GREEN : TFT_RED); 343 | tft_.drawCircle(point.scr_x, point.scr_y, 9, acurate ? TFT_GREEN : TFT_RED); 344 | } 345 | tft_.drawFastHLine(point.scr_x - size + 1, point.scr_y, size - hole, TFT_WHITE); 346 | tft_.drawFastHLine(point.scr_x + hole, point.scr_y, size - hole, TFT_WHITE); 347 | tft_.drawFastVLine(point.scr_x, point.scr_y - size + 1, size - hole, TFT_WHITE); 348 | tft_.drawFastVLine(point.scr_x, point.scr_y + hole, size - hole, TFT_WHITE); 349 | // wait pen up 350 | waitPenUp(); 351 | delay(500); 352 | 353 | return acurate; 354 | } 355 | #endif // TOUCH_USE_SIMPE_TARGET 356 | #endif // TOUCH_USE_USER_CALIBRATION 357 | 358 | #endif // TFT_E_TOUCH_INL 359 | 360 | -------------------------------------------------------------------------------- /TFT_eTouchBase.h: -------------------------------------------------------------------------------- 1 | #ifndef TFT_E_TOUCH_BASE_H 2 | #define TFT_E_TOUCH_BASE_H 3 | 4 | // 5 | // TFT_eTouchBase.h 6 | // 7 | // (C) Copyright Achill Hasler 2019. 8 | // 9 | // Distributed under the Boost Software License, Version 1.0. 10 | // See accompanying file at https://www.boost.org/LICENSE_1_0.txt 11 | // 12 | // 13 | // See TFT_eTouch/docs/html/index.html for documentation. 14 | // 15 | 16 | #include 17 | #include 18 | 19 | #if ARDUINO < 10600 20 | #error "Arduino 1.6.0 or later (SPI library) is required" 21 | #endif 22 | 23 | #define TFT_ETOUCH_VERSION "0.6.0" 24 | 25 | #include 26 | 27 | #ifdef TOUCH_USE_GESTURE 28 | class TFT_eTouchGesture; 29 | #endif 30 | 31 | /** 32 | * @brief touch support base 33 | */ 34 | class TFT_eTouchBase 35 | { 36 | public: 37 | /** 38 | * These are the calibration values for the touchscreen. 39 | * 40 | * When x0 < x1 the touch x axis is in same direction as the tft axis. 41 | * 42 | * when y1 < y0 the touch y axis is in oposite direction as the tft axis. 43 | * 44 | * The calibration values { 250, 3800, 260, 3850, 2 } and { 3800, 250, 3850, 260, 0 } are identical. 45 | * @brief touch calibation 46 | */ 47 | struct Calibation 48 | { 49 | uint16_t x0; ///< x touch value of first display pixel 50 | uint16_t x1; ///< x touch value of last display pixel 51 | uint16_t y0; ///< y touch value of first display pixel 52 | uint16_t y1; ///< y touch value of last display pixel 53 | 54 | uint8_t rel_rotation; ///< relative rotation clockwise from display to touch 55 | }; 56 | 57 | /** 58 | * This struct hold one raw touch measure. 59 | * 60 | * @brief touch raw value 61 | */ 62 | struct Measure 63 | { 64 | uint16_t x; ///< raw x measure range 0..4095 (12 bit) valid (ca 213..3881) 65 | uint16_t y; ///< raw y measure range 0..4095 (12 bit) valid (ca 222..3929) 66 | uint16_t z1; ///< raw z1 measure range 0..4095 (12 bit) valid (ca 29..1748) 67 | uint16_t z2; ///< raw z2 measure range 0..4095 (12 bit) valid (ca 963..3989) 68 | uint16_t rz; ///< calculated resitor value from raw x, z1 and z2 measure, value range (0..rx_plate_*10) when tuched, 0xffff when not tuched. 69 | Measure() : rz(0xffff) {} 70 | }; 71 | 72 | /** 73 | * This struct hold one touch measure with tft coordinates. 74 | * 75 | * @brief tft touch value 76 | */ 77 | struct TouchPoint 78 | { 79 | int16_t x; ///< TFT x position 80 | int16_t y; ///< TFT y position 81 | uint16_t rz; ///< resitance of Touch in ohm 82 | 83 | TouchPoint() : x(0), y(0), rz(0xffff) {} 84 | /// set display position and pressure 85 | inline void set(int16_t _x, int16_t _y, uint16_t _rz) 86 | { x = _x; y = _y; rz = _rz; } 87 | }; 88 | 89 | /** 90 | * Create instance with defaults. 91 | * 92 | * Display and touch must use the same spi bus. 93 | * @brief constructor 94 | * @param cs_pin touch chip select line processor pin 95 | * @param penirq_pin touch penirq line processor pin. 0xff disable touch interrupt 96 | * @param spi used bus 97 | */ 98 | TFT_eTouchBase(uint8_t cs_pin, uint8_t penirq_pin = 0xff, SPIClass& spi = SPI); 99 | 100 | /** 101 | * Initialize the processor cs pin. 102 | * @brief init cs pin 103 | * @param spi_init init spi bus to with default MOSI MISO and SCK 104 | */ 105 | void init(bool spi_init); 106 | 107 | /** 108 | * Get raw position of touch. These values are in orientation of the touchscreen. 109 | * 110 | * The values x, y, z1, z2 and rz are only set if the function returns true. 111 | * @brief touch position 112 | * @param x touch coordinate 113 | * @param y touch coordinate 114 | * @param z1 touch value for calculating rz 115 | * @param z2 touch value for calculating rz 116 | * @param rz calculated pressure in ohm 117 | * @return true when display is tuched 118 | */ 119 | bool getRaw(uint16_t& x, uint16_t& y, uint16_t& z1, uint16_t& z2, uint16_t& rz); 120 | 121 | /** 122 | * Get raw position of touch. These values are in orientation of the touchscreen. 123 | * With TFT_eTouch::transform() you get the display coordinates from the raw measure. 124 | * The value is only set if the function returns true. 125 | * @brief raw touch position 126 | * @param raw value to set 127 | * @return true when display is tuched 128 | */ 129 | bool getRaw(Measure& raw); 130 | 131 | /** 132 | * Wait in this function as long touchscreen is touched. If you must know the touch coordinates while waiting pen up, 133 | * use following code: 134 | @code 135 | while (touch.getXY(x, y)) { 136 | // do something with display coordinates 137 | delay(touch.getMeasureWait() - time used); 138 | } 139 | // or 140 | while (touch.getRaw(x, y, z1, z2, rz)) { 141 | // do something with raw touch coordinates 142 | delay(touch.getMeasureWait() - time used); 143 | } 144 | @endcode 145 | * @brief wait for pen up 146 | */ 147 | void waitPenUp(); 148 | 149 | /** 150 | * Get last calculated RZ value. When display not touched the value is 0xffff. 151 | * As more finger touch the display as lower is this value 152 | * @brief last pressure 153 | * @return last RZ measure in ohm 154 | */ 155 | inline uint16_t getRZ() const; 156 | 157 | /** 158 | * Set the calibration for this touchscreen. 159 | * @brief set calibration 160 | * @param data touch calibration for display 161 | */ 162 | inline void setCalibration(const Calibation& data); 163 | 164 | /** 165 | * Get the calibration for this touchscreen. 166 | * @brief get calibration 167 | * @return data touch calibration for display 168 | */ 169 | inline Calibation& calibration(); 170 | 171 | /** 172 | * Read the calibration for this touchscreen from SPIFLASH or EPROM. 173 | * @brief read calibration 174 | * @param descr filename in flash or adr in eprom 175 | * @return true when data was readed, otherwise data is invalid 176 | */ 177 | bool readCalibration(const char* descr); 178 | 179 | /** 180 | * Write the actual calibration for this touchscreen to SPIFLASH or EPROM. 181 | * @brief write calibration 182 | * @param descr filename in flash or adr in eprom 183 | * @return true when data was written, otherwise calibration is not stored 184 | */ 185 | bool writeCalibration(const char* descr); 186 | 187 | /** 188 | * Set the measure strategie. 189 | * 190 | * Base decicion 191 | * - do i wait until measure is same as measure bevore?
192 | * This is the Figure 10 implmentation from sbaa036.pdf. Set count = 0 193 | * - when not take the n'th measurement?
194 | * This is the Figure 11 implmentation from sbaa036.pdf. Set setAveraging(false) & count = n (0 196 | * Set averaging=true & count = n (0 199 | * When measure of X gives a value in_range() bud Z1 not (touchscreen not touched), its better to start with Z1 (z_first = true) 200 | * 201 | * There are some combinations to now 202 | * - drop_first=0 when count=0 | count>0 & averaging=false
203 | * drop_first=n will be ignored by X and Y measure, Z1 and Z2 measure ignore this when z_once=false. 204 | * - z_once=true
205 | * This set z_first to true, the first Z1 and Z2 measurement will be used when drop_first=0. (otherwise, the drop_first+1 measurement) 206 | * - z_local_min=true
207 | * In Formula of RZ = RX-plate * x/4096 * (Z2/Z1 -1); Z1 is the divisor. When Z1 raise RX shrink. This mean we measure Z1 as long it grow for getting the local minimum of RZ. 208 | * 209 | * 210 | * @brief set measure 211 | * @param drop_first ignore first n measures 212 | * @param z_once read z1 and z2 only once. When this flag is true z_first must be also true. 213 | * @param z_first when true start measure with Z1 otherwise with X 214 | * @param z_local_min when true, get local minimum of RZ 215 | * @param count how many values used by averaging or witch measure is taken. 0 means read until measure is equal last measure 216 | */ 217 | inline void setMeasure(uint8_t drop_first = 0, bool z_once = false, bool z_first = false, bool z_local_min = false, uint8_t count = 0); 218 | 219 | 220 | #ifdef TOUCH_USE_AVERAGING_CODE 221 | /** 222 | * Set averaging mode. 223 | * 224 | * @param averaging when true averaging count measure, otherwise take the count measure as valid measure (if count > 0) 225 | * @param ignore_min_max when true, ignore min and max value on averaging 226 | */ 227 | 228 | inline void setAveraging(bool averaging, bool ignore_min_max = false); 229 | #endif 230 | 231 | /** 232 | * Set valid measure range. We are using the 12bit interface from the chip. Value range is 0 .. 4095, but when measure values near 0 or 4095 indicate that there is no touch. 233 | * @brief valid measure range 234 | * @param min minimal valid measure 235 | * @param max maximal valid measure 236 | */ 237 | inline void setValidRawRange(uint16_t min, uint16_t max); 238 | 239 | /** 240 | * Set the waiting time between two measure. If set to 0 every measure (getRaw(), TFT_eTouch::getXY()) will invoke the chip. 241 | * Otherwise you get the last measure values as long waiting time is not reached. 242 | * @brief set waiting time 243 | * @param ms waiting time in miliseconds between measures 244 | */ 245 | inline void setMeasureWait(uint16_t ms); 246 | /** 247 | * @brief get waiting time 248 | * @return waiting time between measures in miliseconds 249 | */ 250 | inline uint16_t getMeasureWait() const; 251 | 252 | /** 253 | * Set the resistace of the X-plate. The value can be between 300 and 1200. It is used for calculating RZ. If you change this value you have to change also the RZ threshold. 254 | * @brief set RX-plate 255 | * @param ohm RX-plate value 256 | */ 257 | inline void setRXPlate(uint16_t ohm); 258 | 259 | /** 260 | * @brief get RX-plate 261 | * @return RX-plate in ohm 262 | */ 263 | inline uint16_t getRXPlate() const; 264 | 265 | /** 266 | * Set the pressure threshold. If the calculation of RZ is less then this threshold then we have a touch. The value depend on RX-plate and must be also changed when RX-plate is chaged. 267 | * 268 | * Good value is tree time of RX-plate. 269 | * 270 | * Do not set this threshold to 0xffff, this value indicate that there is no touch. 271 | * @brief set threshold 272 | * @param ohm RZ threshold value 273 | */ 274 | inline void setRZThreshold(uint16_t ohm); 275 | /** 276 | * @brief get threshold 277 | * @return RZ threshold value in ohm 278 | */ 279 | inline uint16_t getRZThreshold() const; 280 | 281 | /** 282 | * If you use TOUCH_FILTER_TYPE, you have to call this function on pen-up. Otherwise when TOUCH_FILTER_TYPE is not defined, this function does nothing. 283 | * @brief reset touch buffer 284 | */ 285 | inline void reset(); 286 | 287 | #ifdef TOUCH_USE_USER_CALIBRATION 288 | /** 289 | * If you stay with the pen on the touchscreen the X and Y measure will change a little bit. This value describe the 290 | * tolarable difference of (max-min) from 16 measures. @sa TFT_eTouch::handleTouchCalibrationTarget() 291 | * @brief set acurate difference 292 | * @param raw_difference max difference of target measure 293 | */ 294 | inline void setAcurateDistance(uint16_t raw_difference); 295 | /** 296 | * @brief get acurate difference 297 | * @return acurate difference 298 | */ 299 | inline uint16_t getAcurateDistance() const; 300 | 301 | protected: 302 | /** 303 | * The user has to touch at scr_x and scr_y, the touch coordinate stored then in touch_x and touch_y. 304 | * @brief measure point 305 | */ 306 | struct CalibrationPoint { 307 | int16_t scr_x; ///< x target display position 308 | int16_t scr_y; ///< y target display position 309 | int16_t touch_x; ///< x target touch position measured 310 | int16_t touch_y; ///< y target touch position measured 311 | 312 | /// set target display position 313 | inline void set(int16_t x, int16_t y) 314 | { scr_x = x; scr_y = y; } 315 | 316 | /// print to Serial 317 | void print(); 318 | }; 319 | 320 | /** 321 | * @brief measure CalibrationPoint 322 | * @param point target, to measure. 323 | * @return true when accurate 324 | */ 325 | bool acurateCalibrationTarget(CalibrationPoint& point); 326 | 327 | #else 328 | protected: 329 | #endif // TOUCH_USE_USER_CALIBRATION 330 | 331 | /** 332 | * Fetch all raw touch values in time when only_z1 is false. 333 | * After the call check (rz != 0xffff) if true, then we have a touch. 334 | * 335 | * or 336 | * 337 | * Fetch only z1 in time when only_z1 is true. 338 | * After the call check (raw z1 > 0) if true, then we have a touch. 339 | * @brief update raw in time 340 | * @param only_z1 when true fetch only z1, otherwise x, y, z1, z2 and calculate RZ 341 | */ 342 | void update(bool only_z1); 343 | 344 | inline bool valid(); ///< goes true when tuched (RZ < RZ threshold) 345 | 346 | SPIClass& spi_; ///< used spi bus. display and touch must use the same bus 347 | uint8_t cs_; ///< chip select pin 348 | uint8_t penirq_; ///< penirq pin 349 | 350 | Calibation calibation_; ///< used callibration for transforming touch measure into display pixels 351 | Measure raw_; ///< last touch measure 352 | 353 | #ifdef TOUCH_USE_GESTURE 354 | TFT_eTouchGesture* recognize_; 355 | #endif 356 | 357 | #ifdef TOUCH_USE_PENIRQ_CODE 358 | volatile bool update_allowed_; ///< goes true when penirq happend 359 | #endif // end TOUCH_USE_PENIRQ_CODE 360 | 361 | private: 362 | inline bool is_touched(); ///< goes true when tuched (RZ != 0xffff) 363 | inline bool in_range(uint16_t measure); ///< mesure between raw_valid_min_ and raw_valid_max_? 364 | inline void spi_start(); ///< reserve spi bus and select chip 365 | inline void spi_end(); ///< deselect chip and leave spi bus 366 | /// @sa update() 367 | void fetch_raw(bool only_z1); ///< fetch raw values 368 | 369 | uint8_t drop_first_measures_; ///< ignore first n measures 370 | bool z_once_measure_; ///< measure Z1 & Z2 only once (do not averaging) 371 | bool z_first_measure_; ///< begin with Z1 & Z2 then X Y. when false X Y Z1 Z2 372 | bool z_local_min_measure_; ///< read z1 until value grow 373 | uint8_t count_measure_; ///< averaging/use n measure. when 0 read until measure is equal previous measure 374 | #ifdef TOUCH_USE_AVERAGING_CODE 375 | bool averaging_measure_; ///< when true, averaging count_measure_, when false take the count_measure_ as valid measure (if count_measure_ > 0) 376 | bool ignore_min_max_measure_; ///< when averaging do 2 measure more for dropping min max value in average result 377 | #endif // end TOUCH_USE_AVERAGING_CODE 378 | 379 | uint16_t raw_valid_min_; ///< raw measure minimum value for x, y, z1 and z2 (otherwise it is not touched) 380 | uint16_t raw_valid_max_; ///< raw measure maximum value 381 | 382 | uint32_t last_measure_time_us_; ///< last measure time in microseconds 383 | uint16_t measure_wait_ms_; ///< waiting time in miliseconds between measures 384 | 385 | uint16_t rx_plate_; ///< Resitor value in ohm of x plate (300) 386 | uint16_t rz_threshold_; ///< when RZ < RZ threshold we have a valid touch (default 3*RX-plate) 387 | 388 | #ifdef TOUCH_USE_USER_CALIBRATION 389 | uint16_t acurate_difference_; ///< tolerable noise on X and Y measure for same point 390 | #endif // TOUCH_USE_USER_CALIBRATION 391 | 392 | #ifdef TOUCH_FILTER_TYPE 393 | # ifdef TOUCH_X_FILTER 394 | TOUCH_X_FILTER *x_filter_; 395 | # endif 396 | # ifdef TOUCH_Y_FILTER 397 | TOUCH_Y_FILTER *y_filter_; 398 | # endif 399 | # ifdef TOUCH_Z_FILTER 400 | TOUCH_Z_FILTER *z1_filter_; 401 | TOUCH_Z_FILTER *z2_filter_; 402 | # endif 403 | #endif 404 | }; 405 | 406 | #include 407 | 408 | #ifdef TOUCH_USE_GESTURE 409 | #include 410 | #endif 411 | 412 | #endif // TFT_E_TOUCH_BASE_H 413 | -------------------------------------------------------------------------------- /TFT_eTouchBase.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // TFT_eTouchBase.cpp 3 | // 4 | // (C) Copyright Achill Hasler 2019. 5 | // 6 | // Distributed under the Boost Software License, Version 1.0. 7 | // See accompanying file at https://www.boost.org/LICENSE_1_0.txt 8 | // 9 | // See TFT_eTouch/docs/html/index.html for documentation. 10 | // 11 | 12 | #if (defined (ESP8266) || defined (ESP32)) && !defined (ESP_PLATFORM) 13 | #define ESP_PLATFORM 14 | #endif 15 | 16 | #ifdef ESP_PLATFORM 17 | #include 18 | #ifdef ESP32 19 | #include 20 | #endif 21 | #endif 22 | 23 | #include 24 | 25 | TFT_eTouchBase::TFT_eTouchBase(uint8_t cs_pin, uint8_t penirq_pin, SPIClass& spi) 26 | : spi_(spi) 27 | , cs_(cs_pin) 28 | , penirq_(penirq_pin) 29 | //, raw_.x(0), raw_.y(0) 30 | //, raw_z1_(0), raw_z2_(0) 31 | //, rz_(0xffff) 32 | #ifdef TOUCH_USE_PENIRQ_CODE 33 | , update_allowed_(true) 34 | #endif // end TOUCH_USE_PENIRQ_CODE 35 | , drop_first_measures_(0) 36 | , z_once_measure_(false) 37 | , z_first_measure_(true) 38 | , z_local_min_measure_(false) 39 | , count_measure_(3) 40 | #ifdef TOUCH_USE_AVERAGING_CODE 41 | , averaging_measure_(false) 42 | , ignore_min_max_measure_(false) 43 | #endif // end TOUCH_USE_AVERAGING_CODE 44 | 45 | , raw_valid_min_(25), raw_valid_max_(4000) 46 | , last_measure_time_us_(0), measure_wait_ms_(5) 47 | , rx_plate_(1000/3) 48 | , rz_threshold_(1000) 49 | #ifdef TOUCH_USE_USER_CALIBRATION 50 | , acurate_difference_(10) 51 | #endif // TOUCH_USE_USER_CALIBRATION 52 | { 53 | calibation_ = TOUCH_DEFAULT_CALIBRATION; 54 | 55 | #ifdef TOUCH_FILTER_TYPE 56 | # ifdef TOUCH_X_FILTER 57 | x_filter_ = new TOUCH_X_FILTER(TOUCH_FILTER_TYPE); 58 | if (!x_filter_ && Serial) { 59 | Serial.println("coult not allocate x_filter"); 60 | } 61 | # endif 62 | # ifdef TOUCH_Y_FILTER 63 | y_filter_ = new TOUCH_Y_FILTER(TOUCH_FILTER_TYPE); 64 | if (!y_filter_ && Serial) { 65 | Serial.println("coult not allocate y_filter"); 66 | } 67 | # endif 68 | # ifdef TOUCH_Z_FILTER 69 | z1_filter_ = new TOUCH_Z_FILTER(TOUCH_FILTER_TYPE); 70 | z2_filter_ = new TOUCH_Z_FILTER(TOUCH_FILTER_TYPE); 71 | if ((!z1_filter_ || !z2_filter_) && Serial) { 72 | Serial.println("coult not allocate z_filter"); 73 | } 74 | # endif 75 | #endif 76 | } 77 | 78 | void TFT_eTouchBase::init(bool spi_init) 79 | { 80 | if (spi_init) spi_.begin(); 81 | pinMode(cs_, OUTPUT); 82 | digitalWrite(cs_, HIGH); 83 | 84 | #ifdef ESP_PLATFORM 85 | // check file system 86 | if (!SPIFFS.begin()) { 87 | if (Serial) Serial.println("formating SPIFFS file system"); 88 | 89 | SPIFFS.format(); 90 | if (!SPIFFS.begin()) { 91 | if (Serial) Serial.println("formating SPIFFS file system failed, check Tools/Flash size: != no SPIFFS"); 92 | } 93 | } 94 | #endif 95 | } 96 | 97 | 98 | bool TFT_eTouchBase::getRaw(uint16_t& x, uint16_t& y, uint16_t& z1, uint16_t& z2, uint16_t& rz) 99 | { 100 | update(false); 101 | if (is_touched()) { 102 | x = raw_.x; 103 | y = raw_.y; 104 | z1 = raw_.z1; 105 | z2 = raw_.z2; 106 | rz = raw_.rz; 107 | return true; 108 | } 109 | return false; 110 | } 111 | 112 | bool TFT_eTouchBase::getRaw(Measure& raw) 113 | { 114 | update(false); 115 | if (is_touched()) { 116 | raw = raw_; 117 | return true; 118 | } 119 | return false; 120 | } 121 | 122 | void TFT_eTouchBase::waitPenUp() 123 | { 124 | if (!is_touched()) return; 125 | 126 | while (raw_.z1 != 0) { 127 | delay(measure_wait_ms_ + 1); 128 | update(true); 129 | } 130 | } 131 | 132 | 133 | bool TFT_eTouchBase::readCalibration(const char* descr) 134 | { 135 | bool ret = false; 136 | #ifdef ESP_PLATFORM 137 | if (SPIFFS.exists(descr)) { 138 | File calfile = SPIFFS.open(descr, "r"); 139 | if (calfile) { 140 | Calibation data; 141 | if (calfile.readBytes((char *)&data, sizeof(data)) == sizeof(data)) { 142 | ret = true; 143 | calibation_ = data; 144 | if (Serial) Serial.printf("Calibration: %s x %u, %u, y %u, %u, r %u\n", descr, calibation_.x0, calibation_.x1, calibation_.y0, calibation_.y1, calibation_.rel_rotation); 145 | } 146 | else if (Serial) Serial.println("Calibration file read error"); 147 | calfile.close(); 148 | } 149 | else if (Serial) Serial.println("Calibration file open for read error"); 150 | } 151 | #else 152 | if (Serial) Serial.println("TFT_eTouchBase::readCalibration() not implemented"); 153 | #endif 154 | return ret; 155 | } 156 | 157 | 158 | bool TFT_eTouchBase::writeCalibration(const char* descr) 159 | { 160 | bool ret = false; 161 | #ifdef ESP_PLATFORM 162 | File calfile = SPIFFS.open(descr, "w"); 163 | if (calfile) { 164 | ret = calfile.write((const unsigned char *)&calibation_, sizeof(calibation_)) == sizeof(calibation_); 165 | calfile.close(); 166 | } 167 | if (!ret && Serial) { 168 | Serial.print("Calibration file write error, "); 169 | if (calfile) { 170 | Serial.println("cant write, size mismatch"); 171 | } 172 | else { 173 | Serial.println("cant open"); 174 | } 175 | } 176 | #else 177 | if (Serial) Serial.println("TFT_eTouchBase::writeCalibration() not implemented"); 178 | #endif 179 | if (!ret && Serial) { 180 | Serial.printf("#define TOUCH_DEFAULT_CALIBRATION { %i, %i, %i, %i, %i }\n", 181 | calibation_.x0, calibation_.x1, calibation_.y0, calibation_.y1, calibation_.rel_rotation); 182 | } 183 | return ret; 184 | } 185 | 186 | 187 | void TFT_eTouchBase::update(bool only_z1) 188 | { 189 | #ifdef TOUCH_USE_PENIRQ_CODE 190 | if (!update_allowed_) return; 191 | #else 192 | // cant query PENIRQ for LOW, but when all measures in range then dispay is touched 193 | #endif // end TOUCH_USE_PENIRQ_CODE 194 | 195 | uint32_t now = micros(); 196 | if ( now - last_measure_time_us_ < measure_wait_ms_*1000 ) return; 197 | last_measure_time_us_ = now; 198 | 199 | fetch_raw(only_z1); 200 | 201 | #ifdef TOUCH_USE_PENIRQ_CODE 202 | if (penirq_ != 0xff) { 203 | if (only_z1) update_allowed_ = raw_.z1 > 0; 204 | else update_allowed_ = valid(); 205 | } 206 | #endif // end TOUCH_USE_PENIRQ_CODE 207 | 208 | #ifdef TOUCH_FILTER_TYPE 209 | if (!only_z1 && raw_.rz != 0xffff) { 210 | bool empty = false; 211 | uint16_t val; 212 | # ifdef TOUCH_X_FILTER 213 | val = x_filter_->next(raw_.x); 214 | if (val == 0) { 215 | empty = true; 216 | # ifdef TOUCH_SERIAL_DEBUG 217 | if (Serial) { 218 | Serial.print(x_filter_->filled()); 219 | Serial.print('/'); 220 | Serial.print(x_filter_->size()); 221 | Serial.print(" x filter fill "); 222 | Serial.println(raw_.x); 223 | } 224 | # endif 225 | } 226 | else raw_.x = val; 227 | # endif 228 | # ifdef TOUCH_Y_FILTER 229 | val = y_filter_->next(raw_.y); 230 | if (val == 0) { 231 | empty = true; 232 | # ifdef TOUCH_SERIAL_DEBUG 233 | if (Serial) { 234 | Serial.print(y_filter_->filled()); 235 | Serial.print('/'); 236 | Serial.print(y_filter_->size()); 237 | Serial.print(" y filter fill "); 238 | Serial.println(raw_.y); 239 | } 240 | # endif 241 | } 242 | else raw_.y = val; 243 | # endif 244 | # ifdef TOUCH_Z_FILTER 245 | val = z1_filter_->next(raw_.z1); 246 | if (val == 0) empty = true; 247 | else raw_.z1 = val; 248 | 249 | val = z2_filter_->next(raw_.z2); 250 | if (val == 0) empty = true; 251 | else raw_.z2 = val; 252 | # endif 253 | if (empty) { 254 | raw_.rz = 0xffff; 255 | } 256 | } 257 | #endif 258 | 259 | #ifdef TOUCH_SERIAL_CONVERSATION_TIME 260 | if (Serial) Serial.printf("TFT_eTouchBase::update() %d microseconds\n", micros() - last_measure_time_us_); 261 | #endif 262 | } 263 | 264 | 265 | // Differential Measure (SER/DFR low) 266 | #define X_MEASURE_DFR 0b11010001 // 0b11010011 also works 267 | #define Y_MEASURE_DFR 0b10010001 // 0b10010011 268 | #define Z1_MEASURE_DFR 0b10110001 269 | #define Z2_MEASURE_DFR 0b11000001 270 | 271 | #define OFF_MEASURE 0b10010000 272 | 273 | // Single Ended Measure (SER/DFR high) 274 | #define X_MEASURE_SER 0b11010100 275 | #define Y_MEASURE_SER 0b10010100 276 | #define Z1_MEASURE_SER 0b10110101 277 | #define Z2_MEASURE_SER 0b11000101 278 | 279 | #ifdef TOUCH_USE_DIFFERENTIAL_MEASURE 280 | #define X_MEASURE X_MEASURE_DFR 281 | #define Y_MEASURE Y_MEASURE_DFR 282 | #define Z1_MEASURE Z1_MEASURE_DFR 283 | #define Z2_MEASURE Z2_MEASURE_DFR 284 | #else 285 | #define X_MEASURE X_MEASURE_SER 286 | #define Y_MEASURE Y_MEASURE_SER 287 | #define Z1_MEASURE Z1_MEASURE_SER 288 | #define Z2_MEASURE Z2_MEASURE_SER 289 | #endif 290 | 291 | void TFT_eTouchBase::fetch_raw(bool only_z1) 292 | { 293 | bool has_touch = true; 294 | uint16_t data1, data2; 295 | uint8_t ctrl = X_MEASURE; // X-POSITION Measure 296 | uint8_t drop_cnt = drop_first_measures_; 297 | 298 | if (z_first_measure_ && !z_once_measure_) { 299 | ctrl = Z1_MEASURE; // Z1-POSITION Measure 300 | } 301 | spi_start(); 302 | 303 | if (z_once_measure_) { 304 | z_first_measure_ = true; // then we do it first 305 | spi_.transfer(Z1_MEASURE); // Z1 Measure 306 | while (has_touch && drop_cnt-- > 0) { 307 | data2 = (spi_.transfer16(Z1_MEASURE) >> 3) & 0x0fff; 308 | if (!in_range(data2)) { 309 | has_touch = false; 310 | raw_.z1 = 0; 311 | } 312 | } 313 | drop_cnt = drop_first_measures_; 314 | if (has_touch) { 315 | if (z_local_min_measure_) { // read z1 until grows 316 | data2 = 0; 317 | do { 318 | data1 = data2; 319 | data2 = (spi_.transfer16(Z1_MEASURE) >> 3) & 0x0fff; 320 | } while (data1 < data2); 321 | } 322 | if (only_z1) raw_.z1 = (spi_.transfer16(OFF_MEASURE) >> 3) & 0x0fff; 323 | else raw_.z1 = (spi_.transfer16(Z2_MEASURE) >> 3) & 0x0fff; // Z2 Measure 324 | if (!in_range(raw_.z1)) { 325 | has_touch = false; 326 | raw_.z1 = 0; 327 | } 328 | else { 329 | if (only_z1) { 330 | spi_end(); 331 | return; 332 | } 333 | while (drop_cnt-- > 0) spi_.transfer16(Z2_MEASURE); 334 | drop_cnt = drop_first_measures_; 335 | raw_.z2 = (spi_.transfer16(ctrl) >> 3) & 0x0fff; // X Measure 336 | if (!in_range(raw_.z2)) { 337 | has_touch = false; 338 | } 339 | } 340 | } 341 | } 342 | else { 343 | if (only_z1) ctrl = Z1_MEASURE; 344 | spi_.transfer(ctrl); // X or Z1 Measure 345 | raw_.z1 = 0; 346 | } 347 | 348 | #ifdef TOUCH_USE_AVERAGING_CODE 349 | if (!averaging_measure_) { 350 | ignore_min_max_measure_ = false; 351 | } 352 | #endif // end TOUCH_USE_AVERAGING_CODE 353 | 354 | while (has_touch) { 355 | if (count_measure_ == 0) { 356 | // Figure 10 357 | data1 = 0xffff; 358 | do { 359 | data2 = data1; 360 | data1 = (spi_.transfer16(ctrl) >> 3) & 0x0fff; // X, Y, Z1 or Z2 Measure 361 | } while (in_range(data1) && data1 != data2); // wait until stable 362 | } 363 | else { 364 | // Figure 11 or averaging 365 | #ifdef TOUCH_USE_AVERAGING_CODE 366 | uint16_t min = 0xffff, max = 0; 367 | data2 = count_measure_; 368 | if (averaging_measure_) { 369 | while (has_touch && drop_cnt-- > 0) { 370 | data1 = (spi_.transfer16(ctrl) >> 3) & 0x0fff; 371 | if (!in_range(data1)) { 372 | has_touch = false; 373 | } 374 | } 375 | drop_cnt = drop_first_measures_; 376 | if (!has_touch) break; 377 | if (ignore_min_max_measure_) { 378 | data2 += 2; 379 | } 380 | if (data2 > 16) { // 16 is max! (data1 is 16bit one sum value 12bit, 4bit's for 16 Values) 381 | data2 = 16; 382 | if (ignore_min_max_measure_) count_measure_ = 14; 383 | else count_measure_ = 16; 384 | } 385 | } 386 | #else 387 | data2 = count_measure_; 388 | #endif // end TOUCH_USE_AVERAGING_CODE 389 | data1 = 0; 390 | uint8_t next_ctrl = ctrl; 391 | while (data2--) { 392 | if (data2 == 0) { // last measure of axis, switch ctrl to next 393 | if (ctrl == X_MEASURE) { 394 | next_ctrl = Y_MEASURE; // Y-POSITION Measure 395 | } 396 | else if (ctrl == Y_MEASURE) { 397 | if (z_first_measure_) next_ctrl = OFF_MEASURE; 398 | else next_ctrl = Z1_MEASURE; // Z1-POSITION Measure 399 | } 400 | else if (ctrl == Z1_MEASURE) { 401 | if (!z_local_min_measure_) { // then later 402 | next_ctrl = Z2_MEASURE; // Z2-POSITION Measure 403 | } 404 | else if (only_z1) ctrl = OFF_MEASURE; 405 | } 406 | else if (ctrl == Z2_MEASURE) { // Z2 Measure done 407 | if (!z_first_measure_) next_ctrl = OFF_MEASURE; 408 | else next_ctrl = X_MEASURE; // X-POSITION Measure 409 | } 410 | #ifdef TOUCH_USE_AVERAGING_CODE 411 | if (!averaging_measure_) { 412 | data1 = (spi_.transfer16(next_ctrl) >> 3) & 0x0fff; // take n'th measure of X, Y, Z1 or Z2 413 | } 414 | #else 415 | data1 = (spi_.transfer16(next_ctrl) >> 3) & 0x0fff; // take n'th measure of X, Y, Z1 or Z2 416 | #endif // end TOUCH_USE_AVERAGING_CODE 417 | } 418 | #ifdef TOUCH_USE_AVERAGING_CODE 419 | if (averaging_measure_) { 420 | uint16_t data = (spi_.transfer16(next_ctrl) >> 3) & 0x0fff; // X, Y, Z1 or Z2 Measure 421 | data1 += data; 422 | if (ignore_min_max_measure_) { 423 | if (min > data) min = data; 424 | if (max < data) max = data; 425 | } 426 | } 427 | else { 428 | // dummy read when not n'th measure 429 | if (data2 > 0) { 430 | // spi_.transfer16(next_ctrl); 431 | data1 = (spi_.transfer16(next_ctrl) >> 3) & 0x0fff; 432 | if (!in_range(data1)) { 433 | data2 = 0; 434 | // has_touch = false; 435 | } 436 | } 437 | } 438 | #else 439 | else { 440 | // dummy read when not n'th measure 441 | // spi_.transfer16(next_ctrl); 442 | data1 = (spi_.transfer16(next_ctrl) >> 3) & 0x0fff; 443 | if (!in_range(data1)) { 444 | data2 = 0; 445 | } 446 | } 447 | #endif // end TOUCH_USE_AVERAGING_CODE 448 | } 449 | #ifdef TOUCH_USE_AVERAGING_CODE 450 | if (averaging_measure_) { 451 | if (ignore_min_max_measure_) { 452 | data1 -= (min + max); 453 | } 454 | data1 /= count_measure_; 455 | } 456 | #endif // end TOUCH_USE_AVERAGING_CODE 457 | } 458 | 459 | if (!in_range(data1)) { 460 | has_touch = false; 461 | } 462 | else if (ctrl == X_MEASURE) { // X Measure done 463 | raw_.x = data1; 464 | ctrl = Y_MEASURE; // Y-POSITION Measure 465 | } 466 | else if (ctrl == Y_MEASURE) { // Y Measure done 467 | raw_.y = data1; 468 | if (z_first_measure_) break; 469 | ctrl = Z1_MEASURE; // Z1-POSITION Measure 470 | } 471 | else if (ctrl == Z1_MEASURE) { // Z1 Measure done 472 | if (z_local_min_measure_) { // read z1 until grows 473 | if (raw_.z1 >= data1) { 474 | ctrl = Z2_MEASURE; // Z2-POSITION Measure 475 | if (count_measure_ > 0) { 476 | if (!only_z1) spi_.transfer16(ctrl); // dummy read of last z1, because next transfer16() must return z2 477 | } 478 | } 479 | } 480 | else { 481 | ctrl = Z2_MEASURE; // Z2-POSITION Measure 482 | } 483 | raw_.z1 = data1; 484 | if (only_z1 && ctrl == Z2_MEASURE) { 485 | if (count_measure_ == 0) { 486 | spi_.transfer16(OFF_MEASURE); // set power down mode 487 | } 488 | spi_end(); 489 | return; 490 | } 491 | } 492 | else if (ctrl == Z2_MEASURE) { // Z2 Measure done 493 | raw_.z2 = data1; 494 | if (!z_first_measure_) break; 495 | ctrl = X_MEASURE; // X-POSITION Measure 496 | } 497 | } 498 | if (count_measure_ == 0 || !has_touch) { 499 | spi_.transfer16(OFF_MEASURE); // set power down mode 500 | } 501 | spi_end(); 502 | 503 | #ifdef TOUCH_SERIAL_DEBUG_FETCH // 504 | if (!has_touch) { 505 | if (Serial) { 506 | Serial.print("raw measure out of range value: "); 507 | Serial.print(data1); 508 | Serial.print(" ctrl: 0x"); 509 | Serial.println(ctrl, BIN); 510 | } 511 | } 512 | #endif 513 | if (has_touch && raw_.z1 > 0) { // if z1 is 0 we get a division by 0 exception! 514 | if (raw_.z1 >= raw_.z2) raw_.rz = 0; // more then 2 Finger 515 | // else raw_.rz = (uint16_t)((((int32_t)rx_plate_ * raw_.z2 / raw_.z1) * raw_.x / 4096) - (int32_t)rx_plate_ * raw_.x / 4096); 516 | else raw_.rz = (uint16_t)(((((int32_t)rx_plate_ * raw_.z2 / raw_.z1) * raw_.x) - (int32_t)rx_plate_ * raw_.x) / 4096); 517 | // Formula from ADS7846 pdf: R_TOUCH = Rx-plate * X-Position/4096 * (Z2/Z1 - 1) ; bud work only with float bud i prefer speed 518 | // else raw_.rz = (uint16_t)((float)rx_plate_ * raw_.x / 4096.0 * ((float)raw_.z2 / raw_.z1 - 1.0)); 519 | 520 | // Serial.print("rz: "); Serial.print((uint16_t)((((int32_t)rx_plate_ * raw_.z2 / raw_.z1) * raw_.x / 4096) - (int32_t)rx_plate_ * raw_.x / 4096)); 521 | // Serial.print(", "); Serial.print((uint16_t)(((((int32_t)rx_plate_ * raw_.z2 / raw_.z1) * raw_.x) - (int32_t)rx_plate_ * raw_.x) / 4096)); 522 | // Serial.print(", "); Serial.print((uint16_t)((float)rx_plate_ * raw_.x / 4096.0 * ((float)raw_.z2 / raw_.z1 - 1.0))); Serial.print(" "); 523 | } 524 | else raw_.rz = 0xffff; // indicate 'no touch' 525 | } 526 | 527 | 528 | // -- 529 | #ifdef TOUCH_USE_USER_CALIBRATION 530 | void TFT_eTouchBase::CalibrationPoint::print() 531 | { 532 | #ifdef TOUCH_SERIAL_DEBUG 533 | if (Serial) { 534 | Serial.print("x,y scr: "); 535 | Serial.print(scr_x); 536 | Serial.print(","); 537 | Serial.print(scr_y); 538 | Serial.print(" touch: "); 539 | Serial.print(touch_x); 540 | Serial.print(","); 541 | Serial.println(touch_y); 542 | } 543 | #endif 544 | } 545 | 546 | bool TFT_eTouchBase::acurateCalibrationTarget(CalibrationPoint& point) 547 | { 548 | bool acurate = false; 549 | reset(); // empty FIR filter when used 550 | delay(500); 551 | uint8_t cnt = 0; 552 | uint16_t max_x = 0, max_y = 0; 553 | uint16_t min_x = 0xffff, min_y = 0xffff; 554 | uint16_t sum_x = 0, sum_y = 0; 555 | uint16_t org_wait = getMeasureWait(); 556 | setMeasureWait(0); 557 | while (cnt < 16) { 558 | update(false); 559 | if (valid()) { 560 | sum_x += raw_.x; sum_y += raw_.y; 561 | if (max_x < raw_.x) max_x = raw_.x; 562 | if (max_y < raw_.y) max_y = raw_.y; 563 | if (min_x > raw_.x) min_x = raw_.x; 564 | if (min_y > raw_.y) min_y = raw_.y; 565 | cnt++; 566 | } 567 | else { 568 | delay(1); // wait for the first touch (ESP8266 crash when not waiting) 569 | } 570 | } 571 | setMeasureWait(org_wait); 572 | point.touch_x = (sum_x - max_x - min_x) / 14; 573 | point.touch_y = (sum_y - max_y - min_y) / 14; 574 | 575 | #ifdef TOUCH_SERIAL_DEBUG 576 | if (Serial) { 577 | Serial.printf("acurate on point[%i, %i](%i, %i) dx: %i dy: %i", point.scr_x, point.scr_y, point.touch_x, point.touch_y, max_x - min_x, max_y - min_y); 578 | if ( (max_x - min_x <= getAcurateDistance()) && (max_y - min_y <= getAcurateDistance()) ) { 579 | acurate = true; 580 | Serial.printf(" ok\n"); 581 | } 582 | else { 583 | Serial.printf(" > %i nok\n", getAcurateDistance()); 584 | } 585 | } 586 | else { 587 | if ( (max_x - min_x <= getAcurateDistance()) && (max_y - min_y <= getAcurateDistance()) ) { 588 | acurate = true; 589 | } 590 | } 591 | #else 592 | if ( (max_x - min_x <= getAcurateDistance()) && (max_y - min_y <= getAcurateDistance()) ) { 593 | acurate = true; 594 | } 595 | #endif 596 | return acurate; 597 | } 598 | 599 | #endif // TOUCH_USE_USER_CALIBRATION 600 | -------------------------------------------------------------------------------- /examples/TFT_eSPI/Conways_Life/Conways_Life.ino: -------------------------------------------------------------------------------- 1 | // The Game of Life, also known simply as Life, is a cellular automaton 2 | // devised by the British mathematician John Horton Conway in 1970. 3 | // https://en.wikipedia.org/wiki/Conway's_Game_of_Life 4 | 5 | // touching the corner 6 | // ul: reset this sample 7 | // ur: goto next sample 8 | // ll: speedup animation 9 | // lr: slowdown animation 10 | // touch outside corner will set a cell 11 | 12 | // this sample use touch implemention from TFT_eSPI when TOUCH_CS is defined in User_Setup.h otherwiese TFT_eTouch is used. 13 | 14 | #include 15 | #include 16 | 17 | #include // Hardware-specific library 18 | #ifndef TOUCH_CS 19 | #include 20 | #endif 21 | 22 | TFT_eSPI tft; // Invoke custom library 23 | 24 | #ifndef TOUCH_CS 25 | TFT_eTouch touch(tft, TFT_ETOUCH_CS, 0xff, TFT_eSPI::getSPIinstance()); 26 | #endif 27 | 28 | #define CELL_SIZE 2 // cell in pixel can be 1 29 | #define ROTATION 1 30 | 31 | #ifdef TOUCH_CS 32 | #define CALIBRATION_FILE "/TouchCalibData4" 33 | #else 34 | #define CALIBRATION_FILE "/TFT_eTouch.cal" 35 | #endif 36 | 37 | #define BYTES(bits) (((bits) + 7) / 8) 38 | 39 | class LifeData { 40 | uint16_t x_grid_; 41 | uint16_t y_grid_; 42 | uint8_t* data_; // [XBYTES * GRIDY]; 43 | public: 44 | LifeData(TFT_eSPI& tft, uint8_t cell_size) 45 | : x_grid_(tft.width()/cell_size) 46 | , y_grid_(tft.height()/cell_size) 47 | , data_(new uint8_t[BYTES(x_grid_) * y_grid_]) 48 | {} 49 | 50 | ~LifeData() 51 | { 52 | if (data_) delete data_; 53 | } 54 | 55 | inline void clear() 56 | { 57 | memset(data_, 0, BYTES(x_grid_) * y_grid_); 58 | } 59 | 60 | inline void operator=(const LifeData& d) 61 | { 62 | memcpy(data_, d.data_, BYTES(x_grid_) * y_grid_); 63 | } 64 | 65 | void fill(uint8_t border); 66 | void pattern(uint8_t type, uint16_t x_off, uint16_t y_off); 67 | 68 | inline void set_pos(int16_t x, int16_t y, bool set) 69 | { 70 | if ((x >= x_grid_) || (y >= y_grid_)) return; 71 | uint8_t x_byte = x / 8, x_bit = x % 8; 72 | if (set) data_[x_byte + y * BYTES(x_grid_)] |= _BV(x_bit); 73 | else data_[x_byte + y * BYTES(x_grid_)] &= ~_BV(x_bit); 74 | } 75 | 76 | inline bool get_pos(int16_t x, int16_t y) const 77 | { 78 | if ((x >= x_grid_) || (y >= y_grid_)) return false; 79 | uint8_t x_byte = x / 8, x_bit = x % 8; 80 | return (data_[x_byte + y * BYTES(x_grid_)] & _BV(x_bit)) != 0; 81 | } 82 | 83 | uint8_t neighbors(int16_t x, int16_t y) const; 84 | void print() const; 85 | }; 86 | 87 | 88 | class Life { 89 | TFT_eSPI& tft_; 90 | uint8_t cell_size_; 91 | LifeData grid_, nextgrid_; 92 | uint8_t generation_delay_, border_; 93 | 94 | void draw(); 95 | void compute(); 96 | uint8_t touched(); 97 | public: 98 | Life(TFT_eSPI& tft_, uint8_t cell_size = 3, uint8_t border = 255) 99 | : tft_(tft) 100 | , cell_size_(cell_size) 101 | , grid_(tft_, cell_size) 102 | , nextgrid_(tft_, cell_size) 103 | , generation_delay_(0) 104 | , border_(border) 105 | { } 106 | 107 | void begin(); 108 | void step(); 109 | }; 110 | 111 | void LifeData::pattern(uint8_t type, uint16_t x_off, uint16_t y_off) 112 | { 113 | switch(type) { 114 | case 0: // Lightweight spaceship going right (LWSS) 115 | set_pos(1 + x_off, 1 + y_off, true); 116 | set_pos(1 + x_off, 3 + y_off, true); 117 | set_pos(2 + x_off, 4 + y_off, true); 118 | set_pos(3 + x_off, 4 + y_off, true); 119 | set_pos(4 + x_off, 4 + y_off, true); 120 | set_pos(5 + x_off, 4 + y_off, true); 121 | set_pos(5 + x_off, 3 + y_off, true); 122 | set_pos(5 + x_off, 2 + y_off, true); 123 | set_pos(4 + x_off, 1 + y_off, true); 124 | break; 125 | case 1: // Lightweight spaceship going left (LWSS) 126 | set_pos(6 - 1 + x_off, 1 + y_off, true); 127 | set_pos(6 - 1 + x_off, 3 + y_off, true); 128 | set_pos(6 - 2 + x_off, 4 + y_off, true); 129 | set_pos(6 - 3 + x_off, 4 + y_off, true); 130 | set_pos(6 - 4 + x_off, 4 + y_off, true); 131 | set_pos(6 - 5 + x_off, 4 + y_off, true); 132 | set_pos(6 - 5 + x_off, 3 + y_off, true); 133 | set_pos(6 - 5 + x_off, 2 + y_off, true); 134 | set_pos(6 - 4 + x_off, 1 + y_off, true); 135 | break; 136 | case 2: // Middleweight spaceship going right (MWSS) 137 | set_pos(7 - 4 + x_off, 1 + y_off, true); 138 | set_pos(7 - 2 + x_off, 2 + y_off, true); 139 | set_pos(7 - 6 + x_off, 2 + y_off, true); 140 | set_pos(7 - 1 + x_off, 3 + y_off, true); 141 | set_pos(7 - 1 + x_off, 4 + y_off, true); 142 | set_pos(7 - 6 + x_off, 4 + y_off, true); 143 | set_pos(7 - 1 + x_off, 5 + y_off, true); 144 | set_pos(7 - 2 + x_off, 5 + y_off, true); 145 | set_pos(7 - 3 + x_off, 5 + y_off, true); 146 | set_pos(7 - 4 + x_off, 5 + y_off, true); 147 | set_pos(7 - 5 + x_off, 5 + y_off, true); 148 | break; 149 | case 3: // Middleweight spaceship going left (MWSS) 150 | set_pos(4 + x_off, 1 + y_off, true); 151 | set_pos(2 + x_off, 2 + y_off, true); 152 | set_pos(6 + x_off, 2 + y_off, true); 153 | set_pos(1 + x_off, 3 + y_off, true); 154 | set_pos(1 + x_off, 4 + y_off, true); 155 | set_pos(6 + x_off, 4 + y_off, true); 156 | set_pos(1 + x_off, 5 + y_off, true); 157 | set_pos(2 + x_off, 5 + y_off, true); 158 | set_pos(3 + x_off, 5 + y_off, true); 159 | set_pos(4 + x_off, 5 + y_off, true); 160 | set_pos(5 + x_off, 5 + y_off, true); 161 | break; 162 | case 4: // Heavyweight spaceship going right (HWSS) 163 | y_off += 2; 164 | set_pos(8 - 1 + x_off, 1 + y_off, true); 165 | set_pos(8 - 2 + x_off, 1 + y_off, true); 166 | set_pos(8 - 3 + x_off, 1 + y_off, true); 167 | set_pos(8 - 4 + x_off, 1 + y_off, true); 168 | set_pos(8 - 5 + x_off, 1 + y_off, true); 169 | set_pos(8 - 6 + x_off, 1 + y_off, true); 170 | set_pos(8 - 1 + x_off, 2 + y_off, true); 171 | set_pos(8 - 7 + x_off, 2 + y_off, true); 172 | set_pos(8 - 1 + x_off, 3 + y_off, true); 173 | set_pos(8 - 2 + x_off, 4 + y_off, true); 174 | set_pos(8 - 7 + x_off, 4 + y_off, true); 175 | set_pos(8 - 4 + x_off, 5 + y_off, true); 176 | set_pos(8 - 5 + x_off, 5 + y_off, true); 177 | break; 178 | case 5: // Heavyweight spaceship going left (HWSS) 179 | y_off += 2; 180 | set_pos(1 + x_off, 1 + y_off, true); 181 | set_pos(2 + x_off, 1 + y_off, true); 182 | set_pos(3 + x_off, 1 + y_off, true); 183 | set_pos(4 + x_off, 1 + y_off, true); 184 | set_pos(5 + x_off, 1 + y_off, true); 185 | set_pos(6 + x_off, 1 + y_off, true); 186 | set_pos(1 + x_off, 2 + y_off, true); 187 | set_pos(7 + x_off, 2 + y_off, true); 188 | set_pos(1 + x_off, 3 + y_off, true); 189 | set_pos(2 + x_off, 4 + y_off, true); 190 | set_pos(7 + x_off, 4 + y_off, true); 191 | set_pos(4 + x_off, 5 + y_off, true); 192 | set_pos(5 + x_off, 5 + y_off, true); 193 | break; 194 | case 6: // Glider going down right 195 | set_pos(3 + x_off, 1 + y_off, true); 196 | set_pos(1 + x_off, 2 + y_off, true); 197 | set_pos(3 + x_off, 2 + y_off, true); 198 | set_pos(2 + x_off, 3 + y_off, true); 199 | set_pos(3 + x_off, 3 + y_off, true); 200 | break; 201 | case 7: // Glider going down left 202 | set_pos(4 - 3 + x_off, 1 + y_off, true); 203 | set_pos(4 - 1 + x_off, 2 + y_off, true); 204 | set_pos(4 - 3 + x_off, 2 + y_off, true); 205 | set_pos(4 - 2 + x_off, 3 + y_off, true); 206 | set_pos(4 - 3 + x_off, 3 + y_off, true); 207 | break; 208 | case 8: // Glider going up right 209 | set_pos(3 + x_off, 4 - 1 + y_off, true); 210 | set_pos(1 + x_off, 4 - 2 + y_off, true); 211 | set_pos(3 + x_off, 4 - 2 + y_off, true); 212 | set_pos(2 + x_off, 4 - 3 + y_off, true); 213 | set_pos(3 + x_off, 4 - 3 + y_off, true); 214 | break; 215 | case 9: // Glider going up left 216 | set_pos(4 - 3 + x_off, 4 - 1 + y_off, true); 217 | set_pos(4 - 1 + x_off, 4 - 2 + y_off, true); 218 | set_pos(4 - 3 + x_off, 4 - 2 + y_off, true); 219 | set_pos(4 - 2 + x_off, 4 - 3 + y_off, true); 220 | set_pos(4 - 3 + x_off, 4 - 3 + y_off, true); 221 | break; 222 | case 10: // Blinker 223 | set_pos(1 + x_off, 2 + y_off, true); 224 | set_pos(2 + x_off, 2 + y_off, true); 225 | set_pos(3 + x_off, 2 + y_off, true); 226 | break; 227 | case 11: // Toad 228 | set_pos(3 + x_off, 1 + y_off, true); 229 | set_pos(1 + x_off, 2 + y_off, true); 230 | set_pos(4 + x_off, 2 + y_off, true); 231 | set_pos(1 + x_off, 3 + y_off, true); 232 | set_pos(4 + x_off, 3 + y_off, true); 233 | set_pos(4 + x_off, 4 + y_off, true); 234 | break; 235 | case 12: // Beacon 236 | set_pos(1 + x_off, 1 + y_off, true); 237 | set_pos(2 + x_off, 1 + y_off, true); 238 | set_pos(1 + x_off, 2 + y_off, true); 239 | set_pos(2 + x_off, 2 + y_off, true); 240 | set_pos(3 + x_off, 3 + y_off, true); 241 | set_pos(4 + x_off, 3 + y_off, true); 242 | set_pos(3 + x_off, 4 + y_off, true); 243 | set_pos(4 + x_off, 4 + y_off, true); 244 | break; 245 | case 13: // Pulsar 246 | x_off++; 247 | y_off++; 248 | set_pos(3 + x_off, 1 + y_off, true); 249 | set_pos(4 + x_off, 1 + y_off, true); 250 | set_pos(5 + x_off, 1 + y_off, true); 251 | set_pos(9 + x_off, 1 + y_off, true); 252 | set_pos(10 + x_off, 1 + y_off, true); 253 | set_pos(11 + x_off, 1 + y_off, true); 254 | set_pos(1 + x_off, 3 + y_off, true); 255 | set_pos(6 + x_off, 3 + y_off, true); 256 | set_pos(8 + x_off, 3 + y_off, true); 257 | set_pos(13 + x_off, 3 + y_off, true); 258 | set_pos(1 + x_off, 4 + y_off, true); 259 | set_pos(6 + x_off, 4 + y_off, true); 260 | set_pos(8 + x_off, 4 + y_off, true); 261 | set_pos(13 + x_off, 4 + y_off, true); 262 | set_pos(1 + x_off, 5 + y_off, true); 263 | set_pos(6 + x_off, 5 + y_off, true); 264 | set_pos(8 + x_off, 5 + y_off, true); 265 | set_pos(13 + x_off, 5 + y_off, true); 266 | set_pos(3 + x_off, 6 + y_off, true); 267 | set_pos(4 + x_off, 6 + y_off, true); 268 | set_pos(5 + x_off, 6 + y_off, true); 269 | set_pos(9 + x_off, 6 + y_off, true); 270 | set_pos(10 + x_off, 6 + y_off, true); 271 | set_pos(11 + x_off, 6 + y_off, true); 272 | set_pos(3 + x_off, 8 + y_off, true); 273 | set_pos(4 + x_off, 8 + y_off, true); 274 | set_pos(5 + x_off, 8 + y_off, true); 275 | set_pos(9 + x_off, 8 + y_off, true); 276 | set_pos(10 + x_off, 8 + y_off, true); 277 | set_pos(11 + x_off, 8 + y_off, true); 278 | set_pos(1 + x_off, 9 + y_off, true); 279 | set_pos(6 + x_off, 9 + y_off, true); 280 | set_pos(8 + x_off, 9 + y_off, true); 281 | set_pos(13 + x_off, 9 + y_off, true); 282 | set_pos(1 + x_off, 10 + y_off, true); 283 | set_pos(6 + x_off, 10 + y_off, true); 284 | set_pos(8 + x_off, 10 + y_off, true); 285 | set_pos(13 + x_off, 10 + y_off, true); 286 | set_pos(1 + x_off, 11 + y_off, true); 287 | set_pos(6 + x_off, 11 + y_off, true); 288 | set_pos(8 + x_off, 11 + y_off, true); 289 | set_pos(13 + x_off, 11 + y_off, true); 290 | set_pos(3 + x_off, 13 + y_off, true); 291 | set_pos(4 + x_off, 13 + y_off, true); 292 | set_pos(5 + x_off, 13 + y_off, true); 293 | set_pos(9 + x_off, 13 + y_off, true); 294 | set_pos(10 + x_off, 13 + y_off, true); 295 | set_pos(11 + x_off, 13 + y_off, true); 296 | break; 297 | 298 | case 14: // Pentadecathlon v (period 15) 299 | x_off += 3; 300 | y_off += 3; 301 | set_pos(2 + x_off, 1 + y_off, true); 302 | set_pos(2 + x_off, 2 + y_off, true); 303 | set_pos(1 + x_off, 3 + y_off, true); 304 | set_pos(3 + x_off, 3 + y_off, true); 305 | set_pos(2 + x_off, 4 + y_off, true); 306 | set_pos(2 + x_off, 5 + y_off, true); 307 | set_pos(2 + x_off, 6 + y_off, true); 308 | set_pos(2 + x_off, 7 + y_off, true); 309 | set_pos(1 + x_off, 8 + y_off, true); 310 | set_pos(3 + x_off, 8 + y_off, true); 311 | set_pos(2 + x_off, 9 + y_off, true); 312 | set_pos(2 + x_off, 10 + y_off, true); 313 | break; 314 | 315 | case 15: // Pentadecathlon h (period 15) 316 | x_off += 3; 317 | y_off += 3; 318 | set_pos(3 + x_off, 1 + y_off, true); 319 | set_pos(8 + x_off, 1 + y_off, true); 320 | set_pos(1 + x_off, 2 + y_off, true); 321 | set_pos(2 + x_off, 2 + y_off, true); 322 | set_pos(4 + x_off, 2 + y_off, true); 323 | set_pos(5 + x_off, 2 + y_off, true); 324 | set_pos(6 + x_off, 2 + y_off, true); 325 | set_pos(7 + x_off, 2 + y_off, true); 326 | set_pos(9 + x_off, 2 + y_off, true); 327 | set_pos(10 + x_off, 2 + y_off, true); 328 | set_pos(3 + x_off, 3 + y_off, true); 329 | set_pos(8 + x_off, 3 + y_off, true); 330 | break; 331 | 332 | case 16: // The R-pentomino 333 | set_pos(2 + x_off, 1 + y_off, true); 334 | set_pos(3 + x_off, 1 + y_off, true); 335 | set_pos(1 + x_off, 2 + y_off, true); 336 | set_pos(2 + x_off, 2 + y_off, true); 337 | set_pos(2 + x_off, 3 + y_off, true); 338 | break; 339 | 340 | case 17: // Diehard 341 | set_pos(7 + x_off, 1 + y_off, true); 342 | set_pos(1 + x_off, 2 + y_off, true); 343 | set_pos(2 + x_off, 2 + y_off, true); 344 | set_pos(2 + x_off, 3 + y_off, true); 345 | set_pos(6 + x_off, 3 + y_off, true); 346 | set_pos(7 + x_off, 3 + y_off, true); 347 | set_pos(8 + x_off, 3 + y_off, true); 348 | break; 349 | 350 | case 18: // infinite Line 351 | y_off -= 1; 352 | set_pos(1 + x_off, 1 + y_off, true); 353 | set_pos(2 + x_off, 1 + y_off, true); 354 | set_pos(3 + x_off, 1 + y_off, true); 355 | set_pos(4 + x_off, 1 + y_off, true); 356 | set_pos(5 + x_off, 1 + y_off, true); 357 | set_pos(6 + x_off, 1 + y_off, true); 358 | set_pos(7 + x_off, 1 + y_off, true); 359 | set_pos(8 + x_off, 1 + y_off, true); 360 | set_pos(10 + x_off, 1 + y_off, true); 361 | set_pos(11 + x_off, 1 + y_off, true); 362 | set_pos(12 + x_off, 1 + y_off, true); 363 | set_pos(13 + x_off, 1 + y_off, true); 364 | set_pos(14 + x_off, 1 + y_off, true); 365 | set_pos(18 + x_off, 1 + y_off, true); 366 | set_pos(19 + x_off, 1 + y_off, true); 367 | set_pos(20 + x_off, 1 + y_off, true); 368 | set_pos(27 + x_off, 1 + y_off, true); 369 | set_pos(28 + x_off, 1 + y_off, true); 370 | set_pos(29 + x_off, 1 + y_off, true); 371 | set_pos(30 + x_off, 1 + y_off, true); 372 | set_pos(31 + x_off, 1 + y_off, true); 373 | set_pos(32 + x_off, 1 + y_off, true); 374 | set_pos(33 + x_off, 1 + y_off, true); 375 | set_pos(35 + x_off, 1 + y_off, true); 376 | set_pos(36 + x_off, 1 + y_off, true); 377 | set_pos(37 + x_off, 1 + y_off, true); 378 | set_pos(38 + x_off, 1 + y_off, true); 379 | set_pos(39 + x_off, 1 + y_off, true); 380 | break; 381 | 382 | case 19: // infinite 5x5 383 | set_pos(1 + x_off, 1 + y_off, true); 384 | set_pos(2 + x_off, 1 + y_off, true); 385 | set_pos(3 + x_off, 1 + y_off, true); 386 | set_pos(5 + x_off, 1 + y_off, true); 387 | set_pos(1 + x_off, 2 + y_off, true); 388 | set_pos(4 + x_off, 3 + y_off, true); 389 | set_pos(5 + x_off, 3 + y_off, true); 390 | set_pos(2 + x_off, 4 + y_off, true); 391 | set_pos(3 + x_off, 4 + y_off, true); 392 | set_pos(5 + x_off, 4 + y_off, true); 393 | set_pos(1 + x_off, 5 + y_off, true); 394 | set_pos(3 + x_off, 5 + y_off, true); 395 | set_pos(5 + x_off, 5 + y_off, true); 396 | break; 397 | 398 | default: 399 | break; 400 | } 401 | } 402 | 403 | void LifeData::fill(uint8_t border) 404 | { 405 | int x_off = x_grid_/2; 406 | int y_off = y_grid_/2; 407 | clear(); 408 | switch(border) { 409 | case 255: // The R-pentomino 410 | pattern(16, x_off, y_off); 411 | break; 412 | case 254: // Diehard 413 | pattern(17, x_off-x_off/2, y_off-y_off/2); 414 | pattern(17, x_off, y_off-y_off/2); 415 | pattern(17, x_off+x_off/2, y_off-y_off/2); 416 | 417 | pattern(17, x_off-x_off/2, y_off); 418 | pattern(17, x_off, y_off); 419 | pattern(17, x_off+x_off/2, y_off); 420 | 421 | pattern(17, x_off-x_off/2, y_off+y_off/2); 422 | pattern(17, x_off, y_off+y_off/2); 423 | pattern(17, x_off+x_off/2, y_off+y_off/2); 424 | break; 425 | case 253: // infinite Line 426 | y_off -= 1; 427 | pattern(18, 6, y_off); 428 | break; 429 | case 252: // infinite 5x5 430 | x_off = x_grid_*4/5; 431 | y_off += y_off/2; 432 | pattern(19, x_off, y_off); 433 | break; 434 | case 251: // spaceships 435 | pattern(0, x_off, 3); 436 | pattern(1, x_off, 18 + 3); 437 | pattern(2, x_off, y_off - 18/2); 438 | pattern(3, x_off, y_off + 18/2); 439 | pattern(4, x_off, y_grid_ - 18 - 10); 440 | pattern(5, x_off, y_grid_ - 10); 441 | break; 442 | case 250: // Glider 443 | pattern(6, x_off, y_grid_/2); 444 | pattern(6, x_off + 13, y_grid_/2); 445 | pattern(6, x_off + 2*13, y_grid_/2); 446 | pattern(6, x_off + 3*13, y_grid_/2); 447 | pattern(6, x_off + 4*13, y_grid_/2); 448 | pattern(6, x_off + 5*13, y_grid_/2); 449 | pattern(6, x_off + 6*13, y_grid_/2); 450 | pattern(6, x_off + 7*13, y_grid_/2); 451 | pattern(7, x_off - 13, y_grid_/2); 452 | pattern(7, x_off - 2*13, y_grid_/2); 453 | pattern(7, x_off - 3*13, y_grid_/2); 454 | pattern(7, x_off - 4*13, y_grid_/2); 455 | pattern(7, x_off - 5*13, y_grid_/2); 456 | pattern(7, x_off - 6*13, y_grid_/2); 457 | pattern(7, x_off - 7*13, y_grid_/2); 458 | pattern(7, x_off - 8*13, y_grid_/2); 459 | pattern(8, x_off, y_grid_/2 - 13); 460 | pattern(8, x_off + 13, y_grid_/2 - 13); 461 | pattern(8, x_off + 2*13, y_grid_/2 - 13); 462 | pattern(8, x_off + 3*13, y_grid_/2 - 13); 463 | pattern(8, x_off + 4*13, y_grid_/2 - 13); 464 | pattern(8, x_off + 5*13, y_grid_/2 - 13); 465 | pattern(8, x_off + 6*13, y_grid_/2 - 13); 466 | pattern(8, x_off + 7*13, y_grid_/2 - 13); 467 | pattern(9, x_off - 13, y_grid_/2 - 13); 468 | pattern(9, x_off - 2*13, y_grid_/2 - 13); 469 | pattern(9, x_off - 3*13, y_grid_/2 - 13); 470 | pattern(9, x_off - 4*13, y_grid_/2 - 13); 471 | pattern(9, x_off - 5*13, y_grid_/2 - 13); 472 | pattern(9, x_off - 6*13, y_grid_/2 - 13); 473 | pattern(9, x_off - 7*13, y_grid_/2 - 13); 474 | pattern(9, x_off - 8*13, y_grid_/2 - 13); 475 | break; 476 | case 249: // Oscillators 477 | pattern(10, x_off-20, y_off-20); 478 | pattern(11, x_off, y_off-20); 479 | pattern(12, x_off+20, y_off-20); 480 | 481 | pattern(13, 0, 0); 482 | pattern(14, x_off-10, y_off); 483 | pattern(15, x_off+10, y_off); 484 | break; 485 | case 248: // Random 486 | border = y_grid_ / 4; 487 | default: 488 | if (2 * border > y_grid_ - 40) return; 489 | if (2 * border > x_grid_ - 40) return; 490 | for (int16_t x = border; x < x_grid_-border; x++) { 491 | for (int16_t y = border; y < y_grid_-border; y++) { 492 | if (random(5) == 1) set_pos(x, y, true); 493 | } 494 | } 495 | } 496 | } 497 | 498 | void LifeData::print() const 499 | { 500 | Serial.print("dim: "); 501 | Serial.print(x_grid_); 502 | Serial.print(" / "); 503 | Serial.println(y_grid_); 504 | for (int16_t x = 0; x < x_grid_; x++) { 505 | #if 1 506 | for (int16_t y = 0; y < y_grid_; y++) { 507 | Serial.print("grid["); 508 | Serial.print(x); 509 | Serial.print(", "); 510 | Serial.print(y); 511 | Serial.print("] "); 512 | Serial.print(get_pos(x, y)); 513 | Serial.print(" "); 514 | Serial.println(neighbors(x,y)); 515 | } 516 | #else 517 | Serial.print("grid["); 518 | Serial.print(x); 519 | Serial.print("] "); 520 | 521 | for (int16_t y = 0; y < y_grid_; y++) { 522 | Serial.print("["); 523 | Serial.print(get_pos(x, y)); 524 | Serial.print(" "); 525 | Serial.print(neighbors(x,y)); 526 | Serial.print("]"); 527 | } 528 | Serial.println(""); 529 | #endif 530 | } 531 | } 532 | 533 | uint8_t LifeData::neighbors(int16_t x, int16_t y) const 534 | { 535 | uint8_t neighbors = 0; 536 | 537 | uint16_t xpos = (x + 1) % x_grid_; 538 | uint16_t xneg = (x + x_grid_ - 1) % x_grid_; 539 | 540 | uint16_t ypos = (y + 1) % y_grid_; 541 | uint16_t yneg = (y + y_grid_ - 1) % y_grid_; 542 | 543 | if (get_pos(xneg, y) ) neighbors++; 544 | if (get_pos(xneg, yneg)) neighbors++; 545 | if (get_pos(x, yneg)) neighbors++; 546 | if (get_pos(xpos, yneg)) neighbors++; 547 | if (get_pos(xpos, y) ) neighbors++; 548 | if (get_pos(xpos, ypos)) neighbors++; 549 | if (get_pos(x, ypos)) neighbors++; 550 | if (get_pos(xneg, ypos)) neighbors++; 551 | 552 | return neighbors; 553 | } 554 | 555 | void Life::draw() 556 | { 557 | uint16_t color = TFT_WHITE; 558 | for (int16_t x = 0; x < tft_.width()/cell_size_; x++) { 559 | for (int16_t y = 0; y < tft_.height()/cell_size_; y++) { 560 | if (grid_.get_pos(x, y) != nextgrid_.get_pos(x, y)) { 561 | if (nextgrid_.get_pos(x, y)) color = TFT_WHITE; 562 | else color = TFT_BLACK; 563 | tft_.fillRect(cell_size_ * x, cell_size_ * y, cell_size_, cell_size_, color); 564 | } 565 | } 566 | } 567 | grid_ = nextgrid_; 568 | } 569 | 570 | void Life::compute() 571 | { 572 | nextgrid_.clear(); 573 | for (int16_t x = 0; x < tft_.width()/cell_size_; x++) { 574 | for (int16_t y = 0; y < tft_.height()/cell_size_; y++) { 575 | bool active = grid_.get_pos(x, y); 576 | uint8_t neighbors = grid_.neighbors(x, y); 577 | if (active) { 578 | if (neighbors >= 2 && neighbors <= 3) { 579 | // stay active 580 | nextgrid_.set_pos(x, y, true); 581 | } 582 | } 583 | else if (neighbors == 3) { 584 | // get active 585 | nextgrid_.set_pos(x, y, true); 586 | } 587 | } 588 | } 589 | } 590 | 591 | uint8_t Life::touched() 592 | { 593 | const int offset = 20; 594 | #ifdef TOUCH_CS 595 | uint16_t x, y; 596 | if (tft_.getTouch(&x, &y)) { 597 | #else 598 | int16_t x, y; 599 | if (touch.getXY(x, y)) { 600 | #endif 601 | if (x < offset) { 602 | if (y < offset) return 2; // left upper corner 603 | else if (y > tft_.height() - offset) return 4; // left lower corner 604 | } 605 | if (x > tft_.width() - offset) { 606 | if (y < offset) return 5; // right upper corner 607 | else if (y > tft_.height() - offset) return 3; // right lower corner 608 | } 609 | nextgrid_.set_pos(x/cell_size_, y/cell_size_, true); 610 | return 1; // anywhere 611 | } 612 | return 0; // nowhere 613 | } 614 | 615 | void Life::begin() 616 | { 617 | // Display a simple splash screen 618 | tft_.fillScreen(TFT_BLACK); 619 | tft_.setTextSize(2); 620 | tft_.setTextColor(TFT_WHITE); 621 | tft_.setCursor(35, 5); 622 | tft_.println(F("Arduino")); 623 | tft_.setCursor(35, 35); 624 | tft_.println(F("Conway's")); 625 | tft_.setCursor(35, 65); 626 | tft_.println(F("Game of Life")); 627 | 628 | tft_.setCursor(35, 95); 629 | tft_.println(F("Touch corners")); 630 | 631 | tft_.setCursor(35, 125); 632 | switch(border_) { 633 | case 255: // The R-pentomino 634 | tft_.println(F("The R-pentomino")); 635 | break; 636 | case 254: // Diehard 637 | tft_.println(F("Diehard")); 638 | break; 639 | case 253: // infinite Line 640 | tft_.println(F("infinite Line")); 641 | break; 642 | case 252: // infinite 5x5 643 | tft_.println(F("infinite 5x5")); 644 | break; 645 | case 251: // spaceships 646 | tft_.println(F("Spaceships")); 647 | break; 648 | case 250: // Glider 649 | tft_.println(F("Glider")); 650 | break; 651 | case 249: // Oscillators 652 | tft_.println(F("Oscillators")); 653 | break; 654 | case 248: // Random 655 | tft_.println(F("Random")); 656 | break; 657 | default: 658 | tft_.println(F("Border")); 659 | break; 660 | } 661 | delay(2000); 662 | 663 | tft_.fillScreen(TFT_BLACK); 664 | grid_.clear(); 665 | nextgrid_.fill(border_); 666 | } 667 | 668 | void Life::step() 669 | { 670 | draw(); 671 | compute(); 672 | switch(touched()) { 673 | case 0: // nowhere 674 | break; 675 | case 1: // anywhere 676 | break; 677 | case 2: // left upper corner 678 | begin(); 679 | break; 680 | case 3: // right lower corner 681 | if (generation_delay_ < 255) generation_delay_++; 682 | break; 683 | case 4: // left lower corner 684 | if (generation_delay_ > 0) generation_delay_--; 685 | break; 686 | case 5: // right upper corner 687 | if (border_ >= 248) { 688 | if (--border_ < 248) { 689 | border_ = 255; 690 | } 691 | begin(); 692 | } 693 | break; 694 | default: break; 695 | } 696 | delay(generation_delay_ * 20); 697 | } 698 | 699 | Life* life = 0; 700 | 701 | 702 | void setup() 703 | { 704 | Serial.begin(115200); 705 | delay(2000); 706 | //Set up the display 707 | tft.init(); 708 | tft.setRotation(ROTATION); 709 | 710 | 711 | #ifndef TOUCH_CS 712 | touch.init(); 713 | 714 | // untouched: 35 us touched: 136 us 715 | // touch.setMeasure(0, false, true, false, 3); // constructor defaults (take third measure, start with z axis) 716 | // touch.setAveraging(false, false); // constructor defaults (without averaging) 717 | 718 | // untouched: 35 us touched: 56 us 719 | // touch.setMeasure(0, true, true, false, 1); // Differential mode fastest (each axis read only once, may work) 720 | 721 | // untouched: 35 us touched: 77 us 722 | // touch.setMeasure(1, true, true, false, 1); // Differential mode faster (take second z bud first x, y. may work) 723 | 724 | // untouched: 35 us touched: d 190 max 600 us 725 | // touch.setMeasure(1, true, true, true); // z with local min, acurate x,y, 35 us tot d 200 max 600 726 | 727 | // untouched: 35 us touched: d 220 max 600 us 728 | // touch.setMeasure(0, false, true); // z first, acurate z,x,y 729 | 730 | // untouched: 35 us touched: d 190 max 600 us 731 | // touch.setMeasure(2, true, true, false); // z first 3end, acurate x,y 732 | 733 | // untouched: 35 us touched: 95 us 734 | // touch.setMeasure(1, true, true, false, 2); // z first, take 2'th z,x,y 735 | 736 | // untouched: 35 us touched: 95 us 737 | touch.setMeasure(0, false, true, false, 2); // z first, take 2'th z,x,y 738 | 739 | // untouched: 35 us touched: 1302 us 740 | // touch.setMeasure(10, false, true, true, 14); // slowest (drop 10(..255) and additional 16 measures. averaging 14 with min max value) 741 | // touch.setAveraging(true, true); 742 | 743 | // untouched: 35 us touched: 334 us 744 | // touch.setMeasure(3, false, true, false, 3); // drop 3 averaging 3 (+2 min max) 745 | // touch.setAveraging(true, true); // averaging on (+2 min max) 746 | 747 | #endif 748 | 749 | tft.fillScreen(TFT_WHITE); 750 | 751 | tft.setCursor(20, 0, 2); 752 | tft.setTextColor(TFT_BLACK, TFT_WHITE); 753 | tft.setTextSize(1); 754 | tft.println("calibration run"); 755 | 756 | //SPIFFS.remove(CALIBRATION_FILE); 757 | 758 | // check if calibration file exists 759 | #ifdef TOUCH_CS 760 | uint16_t cal_data[5]; 761 | bool has_cal_file = false; 762 | 763 | // check file system 764 | if (!SPIFFS.begin()) { 765 | Serial.println("formating file system"); 766 | 767 | SPIFFS.format(); 768 | SPIFFS.begin(); 769 | } 770 | 771 | if (SPIFFS.exists(CALIBRATION_FILE)) { 772 | File f = SPIFFS.open(CALIBRATION_FILE, "r"); 773 | if (f) { 774 | if (f.readBytes((char *)cal_data, 10) == 10) has_cal_file = true; 775 | if (has_cal_file) { 776 | Serial.printf("Calibration: x %u, %u, y %u, %u, opt %u\n", cal_data[0], cal_data[1], cal_data[2], cal_data[3], cal_data[4]); 777 | } 778 | else Serial.println("Calibration File read error"); 779 | f.close(); 780 | } 781 | } 782 | if (has_cal_file) { 783 | // calibration data valid 784 | tft.setTouch(cal_data); 785 | } 786 | else { 787 | // data not valid. recalibrate 788 | tft.calibrateTouch(cal_data, TFT_WHITE, TFT_RED, 15); 789 | // store data 790 | File f = SPIFFS.open(CALIBRATION_FILE, "w"); 791 | if (f) { 792 | f.write((const unsigned char *)cal_data, 10); 793 | f.close(); 794 | } 795 | } 796 | #else 797 | if (!touch.readCalibration(CALIBRATION_FILE)) { 798 | #ifdef TOUCH_USE_USER_CALIBRATION 799 | TFT_eTouchBase::Calibation calibation; // = { 265, 3790, 264, 3850, 2 }; // x and y axes have same direction on touch & display 800 | touch.getUserCalibration(calibation, 4); 801 | touch.setCalibration(calibation); 802 | touch.writeCalibration(CALIBRATION_FILE); 803 | #else 804 | Serial.printf("Calibration not readed %s take default configuration. Store a valid configuration with eTouch_edit\n", CALIBRATION_FILE); 805 | touch.calibration() = { 265, 3790, 264, 3850, 2 }; 806 | #endif 807 | } 808 | #endif 809 | 810 | tft.fillScreen(TFT_BLACK); 811 | life = new Life(tft, CELL_SIZE); 812 | life->begin(); 813 | } 814 | 815 | void loop() { 816 | life->step(); 817 | } 818 | --------------------------------------------------------------------------------