From d7c40afa4334c25c967e8a83401ac1a968a0c084 Mon Sep 17 00:00:00 2001 From: derekhe Date: Sat, 27 Jun 2015 18:15:41 +0800 Subject: Add touch support. --- .gitignore | 1 + 7inch.patch | 17 +++++++++++++ README.md | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++ install.sh | 14 +++++++++++ touch.py | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ touch.sh | 62 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 251 insertions(+) create mode 100644 .gitignore create mode 100644 7inch.patch create mode 100644 README.md create mode 100644 install.sh create mode 100644 touch.py create mode 100644 touch.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bc8a670 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea/* \ No newline at end of file diff --git a/7inch.patch b/7inch.patch new file mode 100644 index 0000000..5c47a13 --- /dev/null +++ b/7inch.patch @@ -0,0 +1,17 @@ +diff -r C:\Users\hesicong\Desktop\compare\config.txt C:\Users\hesicong\Desktop\compare\touch\boot\config.txt +25c24 +< #hdmi_force_hotplug=1 +--- +> hdmi_force_hotplug=1 +27,29c26,30 +< # uncomment to force a specific HDMI mode (this will force VGA) +< #hdmi_group=1 +< #hdmi_mode=1 +--- +> # uncomment to force a specific HDMI mode (here we are forcing 800x480!) +> hdmi_group=2 +> hdmi_mode=1 +> hdmi_mode=87 +> hdmi_cvt 800 480 60 6 0 0 0 +30a32 +> max_usb_current=1 diff --git a/README.md b/README.md new file mode 100644 index 0000000..935721c --- /dev/null +++ b/README.md @@ -0,0 +1,73 @@ +WaveShare 7-inch user space driver +=================================== +I brought a [WaveShare 7-inch HDMI LCD](http://www.waveshare.net/shop/7inch-HDMI-LCD-B.htm) and it provides a USB touchscreen. +But the company only provide binary driver and images, which is quite bad. Installing binary driver will break self compiled kernel, and you can't get updated kernels. +I asked the company to provide the source code but they refused. They said they won't provide any source code because other companies can copy very fast so that their products can't sell out at good price. + +OK. If they company won't want to provide anything, that's fine. I finally find out we can still drive the touchscreen by writing a user space driver. + +Tested using official image: 2015-05-05-raspbian-wheezy.img + +# Install +ssh into your raspiberry + +clone this repo into any dir,then + +``` +chmod +x install.sh +./install.sh + +sudo restart +``` + +# How do I hack it +By looking at the dmesg information, we can see it is installed as a hid-generic driver, the vendor is 0x0eef(eGalaxy) and product is 0x0005. +0x0005 can't be found anywhere, I think the company wrote their own driver to support this. + +## dmesg infomation +``` +[ 3.518144] usb 1-1.5: new full-speed USB device number 4 using dwc_otg +[ 3.606036] udevd[174]: starting version 175 +[ 3.631476] usb 1-1.5: New USB device found, idVendor=0eef, idProduct=0005 +[ 3.641195] usb 1-1.5: New USB device strings: Mfr=1, Product=2, SerialNumber=3 +[ 3.653540] usb 1-1.5: Product: By ZH851 +[ 3.659956] usb 1-1.5: Manufacturer: RPI_TOUCH +[ 3.659967] usb 1-1.5: SerialNumber: \xffffffc2\xffffff84\xffffff84\xffffffc2\xffffffa0\xffffffa0B54711U335 +[ 3.678577] hid-generic 0003:0EEF:0005.0001: hiddev0,hidraw0: USB HID v1.10 Device [RPI_TOUCH By ZH851] on usb-bcm2708_usb-1.5/input0 +``` +kernel config provide us more clue: +``` +CONFIG_USB_EGALAX_YZH=y +``` + +It is really a eGalaxy based device. Google this config but found nothing. I don't have a eGalxy to compare, maybe waveshare's touchscreen is only modifed the product id. + +Then I look at the response of hidraw driver: + +## hidraw driver analysis +``` +pi@raspberrypi ~/python $ sudo xxd -c 25 /dev/hidraw0 +0000000: aa00 0000 0000 bb00 0000 0000 0000 0000 0000 0000 0000 0000 00 ......................... +0000019: aa01 00c5 0134 bb01 0000 0000 0000 0000 0000 0000 0000 0000 cc .....4................... +``` + +You can try by your self, by moving the figure on the screen you will notice the value changes. +Take one for example: +``` +0000271: aa01 00e4 0139 bb01 01e0 0320 01e0 0320 01e0 0320 01e0 0320 cc .....9..... ... ... ... . +``` + +"aa" is start of the command, "01" means clicked while "00" means unclicked. "00e4" and "0139" is the X,Y position (HEX). +"bb" is start of multi-touch, and the following bytes are the position of each point. + +## Write the driver +I use python to read from hidraw driver and then use uinput to emulate the mouse. It is quite easy to do. Please look at the source code. + +## Other systems +I think this driver can work in any linux system with hidraw and uinput driver support. + +## Calibration +I didn't write any calibration because my touchscreen is working fine. + +## Multitouch +Hmmm, I'm lazy, do it yourself. \ No newline at end of file diff --git a/install.sh b/install.sh new file mode 100644 index 0000000..7059a12 --- /dev/null +++ b/install.sh @@ -0,0 +1,14 @@ +#!/bin/bash + +chmod +x *.sh *.py +sudo patch -b /boot/config.txt 7inch.patch +sudo apt-get install -y python3-pip libudev-dev +sudo pip-3.2 install python-uinput pyudev + +sudo cp touch.py /usr/bin/ +sudo cp touch.sh /etc/init.d/ + +chmod +x /usr/bin/touch.py +chmod +x /etc/init.d/touch.sh + +sudo update-rc.d touch.sh defaults \ No newline at end of file diff --git a/touch.py b/touch.py new file mode 100644 index 0000000..f32a28c --- /dev/null +++ b/touch.py @@ -0,0 +1,84 @@ +#!/usr/bin/env python3 + +import struct +import time +import math +import glob +import uinput +import pyudev +import os + +# Wait and find devices +def read_and_emulate_mouse(deviceFound): + with open(deviceFound, 'rb') as f: + print("Read buffer") + + device = uinput.Device([ + uinput.BTN_LEFT, + uinput.BTN_RIGHT, + uinput.ABS_X, + uinput.ABS_Y, + ]) + + clicked = False + rightClicked = False + (lastX, lastY) = (0, 0) + startTime = time.time() + + while True: + b = f.read(25) + (tag, btnLeft, x, y) = struct.unpack_from('>c?HH', b) + print(btnLeft, x, y) + + if btnLeft: + device.emit(uinput.ABS_X, x, True) + device.emit(uinput.ABS_Y, y, True) + + if not clicked: + print("Left click") + device.emit(uinput.BTN_LEFT, 1) + clicked = True + startTime = time.time() + (lastX, lastY) = (x, y) + + duration = time.time() - startTime + movement = math.sqrt(pow(x - lastX, 2) + pow(y - lastY, 2)) + + if clicked and (not rightClicked) and (duration > 1) and (movement < 20): + print("Right click") + device.emit(uinput.BTN_RIGHT, 1) + device.emit(uinput.BTN_RIGHT, 0) + rightClicked = True + else: + print("Release") + clicked = False + rightClicked = False + device.emit(uinput.BTN_LEFT, 0) + + +if __name__ == "__main__": + os.system("modprobe uinput") + os.system("chmod 666 /dev/hidraw*") + os.system("chmod 666 /dev/uinput*") + + while True: + # try: + print("Waiting device") + hidrawDevices = glob.glob("/dev/hidraw*") + + context = pyudev.Context() + + deviceFound = None + for hid in hidrawDevices: + device = pyudev.Device.from_device_file(context, hid) + if "0EEF:0005" in device.device_path: + deviceFound = hid + + if deviceFound: + print("Device found", deviceFound) + read_and_emulate_mouse(deviceFound) + # except: + # print("Error:", sys.exc_info()) + # pass + # finally: + # time.sleep(1) diff --git a/touch.sh b/touch.sh new file mode 100644 index 0000000..07b23a8 --- /dev/null +++ b/touch.sh @@ -0,0 +1,62 @@ +#!/bin/sh + +### BEGIN INIT INFO +# Provides: myservice +# Required-Start: $remote_fs $syslog +# Required-Stop: $remote_fs $syslog +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Put a short description of the service here +# Description: Put a long description of the service here +### END INIT INFO + +# Change the next 3 lines to suit where you install your script and what you want to call it +DIR=/usr/bin/ +DAEMON=$DIR/touch.py +DAEMON_NAME=touch + +# Add any command line options for your daemon here +DAEMON_OPTS="" + +# This next line determines what user the script runs as. +# Root generally not recommended but necessary if you are using the Raspberry Pi GPIO from Python. +DAEMON_USER=root + +# The process ID of the script when it runs is stored here: +PIDFILE=/var/run/$DAEMON_NAME.pid + +. /lib/lsb/init-functions + +do_start () { + log_daemon_msg "Starting system $DAEMON_NAME daemon" + start-stop-daemon --start --background --pidfile $PIDFILE --make-pidfile --user $DAEMON_USER --chuid $DAEMON_USER --startas $DAEMON -- $DAEMON_OPTS + log_end_msg $? +} +do_stop () { + log_daemon_msg "Stopping system $DAEMON_NAME daemon" + start-stop-daemon --stop --pidfile $PIDFILE --retry 10 + log_end_msg $? +} + +case "$1" in + + start|stop) + do_${1} + ;; + + restart|reload|force-reload) + do_stop + do_start + ;; + + status) + status_of_proc "$DAEMON_NAME" "$DAEMON" && exit 0 || exit $? + ;; + + *) + echo "Usage: /etc/init.d/$DAEMON_NAME {start|stop|restart|status}" + exit 1 + ;; + +esac +exit 0 -- cgit v1.2.1