/ adafruit_touchscreen.py
adafruit_touchscreen.py
  1  # The MIT License (MIT)
  2  #
  3  # Copyright (c) 2019 ladyada for Adafruit
  4  #
  5  # Permission is hereby granted, free of charge, to any person obtaining a copy
  6  # of this software and associated documentation files (the "Software"), to deal
  7  # in the Software without restriction, including without limitation the rights
  8  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9  # copies of the Software, and to permit persons to whom the Software is
 10  # furnished to do so, subject to the following conditions:
 11  #
 12  # The above copyright notice and this permission notice shall be included in
 13  # all copies or substantial portions of the Software.
 14  #
 15  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 20  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 21  # THE SOFTWARE.
 22  """
 23  `adafruit_touchscreen`
 24  ================================================================================
 25  
 26  CircuitPython library for 4-wire resistive touchscreens
 27  
 28  
 29  * Author(s): ladyada
 30  
 31  Implementation Notes
 32  --------------------
 33  
 34  **Hardware:**
 35  
 36  
 37  **Software and Dependencies:**
 38  
 39  * Adafruit CircuitPython firmware for the supported boards:
 40    https://github.com/adafruit/circuitpython/releases
 41  """
 42  
 43  __version__ = "0.0.0-auto.0"
 44  __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_Touchscreen.git"
 45  
 46  from digitalio import DigitalInOut
 47  from analogio import AnalogIn
 48  
 49  
 50  def map_range(x, in_min, in_max, out_min, out_max):
 51      """
 52      Maps a number from one range to another.
 53      Note: This implementation handles values < in_min differently than arduino's map function does.
 54      :return: Returns value mapped to new range
 55      :rtype: float
 56      """
 57      mapped = (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min
 58      if out_min <= out_max:
 59          return max(min(mapped, out_max), out_min)
 60      return min(max(mapped, out_max), out_min)
 61  
 62  
 63  class Touchscreen:
 64      """A driver for common and inexpensive resistive touchscreens. Analog input
 65      capable pins are required to read the intrinsic potentiometers"""
 66  
 67      def __init__(
 68          self,
 69          x1_pin,
 70          x2_pin,
 71          y1_pin,
 72          y2_pin,
 73          *,
 74          x_resistance=None,
 75          samples=4,
 76          z_threshhold=10000,
 77          calibration=None,
 78          size=None
 79      ):
 80          """Create the Touchscreen object. At a minimum you need the 4 pins
 81          that will connect to the 4 contacts on a screen. X and Y are just our
 82          names, you can rotate and flip the data if you like. All pins must be
 83          capable of becoming DigitalInOut pins. 'y2_pin', 'x1_pin' and 'x2_pin'
 84  	must also be capable of becoming AnalogIn pins.
 85          If you know the resistance across the x1 and x2 pins when not touched,
 86          pass that in as 'x_resistance'.
 87          By default we oversample 4 times, change by adjusting 'samples' arg.
 88          We can also detect the 'z' threshold, how much its prssed. We don't
 89          register a touch unless its higher than 'z_threshold'
 90          'calibration' is a tuple of two tuples, the default is
 91          ((0, 65535), (0, 65535)). The numbers are the min/max readings for the
 92          X and Y coordinate planes, respectively. To figure these out, pass in
 93          no calibration value and read the raw values out while touching the
 94          panel.
 95          'size' is a tuple that gives the X and Y pixel size of the underlying
 96          screen. If passed in, we will automatically scale/rotate so touches
 97          correspond to the graphical coordinate system.
 98          """
 99          self._xm_pin = x1_pin
100          self._xp_pin = x2_pin
101          self._ym_pin = y1_pin
102          self._yp_pin = y2_pin
103          self._rx_plate = x_resistance
104          self._xsamples = [0] * samples
105          self._ysamples = [0] * samples
106          if not calibration:
107              calibration = ((0, 65535), (0, 65535))
108          self._calib = calibration
109          self._size = size
110          self._zthresh = z_threshhold
111  
112      @property
113      def touch_point(self):  # pylint: disable=too-many-locals
114          """A tuple that represents the x, y and z (touch pressure) coordinates
115          of a touch. Or, None if no touch is detected"""
116          with DigitalInOut(self._yp_pin) as y_p:
117              with DigitalInOut(self._ym_pin) as y_m:
118                  with AnalogIn(self._xp_pin) as x_p:
119                      y_p.switch_to_output(True)
120                      y_m.switch_to_output(False)
121                      for i in range(len(self._xsamples)):
122                          self._xsamples[i] = x_p.value
123          x = sum(self._xsamples) / len(self._xsamples)
124          x_size = 65535
125          if self._size:
126              x_size = self._size[0]
127          x = int(map_range(x, self._calib[0][0], self._calib[0][1], 0, x_size))
128  
129          with DigitalInOut(self._xp_pin) as x_p:
130              with DigitalInOut(self._xm_pin) as x_m:
131                  with AnalogIn(self._yp_pin) as y_p:
132                      x_p.switch_to_output(True)
133                      x_m.switch_to_output(False)
134                      for i in range(len(self._ysamples)):
135                          self._ysamples[i] = y_p.value
136          y = sum(self._ysamples) / len(self._ysamples)
137          y_size = 65535
138          if self._size:
139              y_size = self._size[1]
140          y = int(map_range(y, self._calib[1][0], self._calib[1][1], 0, y_size))
141  
142          z_1 = z_2 = z = None
143          with DigitalInOut(self._xp_pin) as x_p:
144              x_p.switch_to_output(False)
145              with DigitalInOut(self._ym_pin) as y_m:
146                  y_m.switch_to_output(True)
147                  with AnalogIn(self._xm_pin) as x_m:
148                      z_1 = x_m.value
149                  with AnalogIn(self._yp_pin) as y_p:
150                      z_2 = y_p.value
151          # print(z_1, z_2)
152          z = 65535 - (z_2 - z_1)
153          if z > self._zthresh:
154              return (x, y, z)
155          return None