3M Microtouch touchscreen emulator in Python

A colleague asked about virtualising a touch screen DOS application, designed for use with a microtouch touch screen interfacing over RS232. The familiar story of the monitor / old PC hardware dying and replacing it seemed a shorter term solution to a problem that could be solved with virtualisation or emulation. Note: This software no longer has any external hardware connected.

The proposal:

  1. Virtualise or emulate the application within the windows environment.
  2. Link the application to a serial port on the host system (virtual or real)
  3. Write a touch screen emulator to simulate presses using mouse clicks within the application window.

VirtualBox and VMWare Workstation are two virtualisation solutions that support mapping host serial ports to virtual ports. After attempting to use both of these, the timing critical nature of this specific DOS application upon startup didn’t like [my suspected]  lag of the virtualised ports. Running it again in DOSBox, a x86 emulator, resolved this issue. Virtual Serial Port Emulator is a tool to create virtual ports that can act as a connector between programs. This maps the serial streams from the touchscreen emulator to the serial port DOSBox is looking for in this case (how-to configure DOSBox serial ports).

VSPE-screenshot

Finally, the touch screen emulation is written in Python as a quick and simple way to test it out. PyHook is used to capture mouse clicks and mouse position, while PySerial is used for all serial communication. This program  will firstly respond to a handful of serial commands that are required upon loading the system in DOSBox. Once in, it will ask you to calibrate the emulated touch screen by clicking the top left and bottom right of the application window. It will only action on clicks in the specified window, in this case, DOSBox. Each click within this area will be mapped across the standard 10 bit vertical and horizontal touch screen space and sent to the serial port specified.

# Microtouch emulator
# Copyright (C) 2014 Oliver S
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.

import serial
import pyHook
import pythoncom

left_click = 0
    
# check that value passed is within specified range
def set_limits(n, minn, maxn):
    if n < minn:
        return minn
    elif n > maxn:
        return maxn
    else:
        return n

# on each mouse click
def OnMouseEvent(event):

    # record current mouse position
    x,y = event.Position

    global left_click,tlp_x,tlp_y,brp_x,brp_y # tlp = top left position, brp = bottom right position

    # check to see if we're clicking in the dosbox window
    if event.WindowName and "DOSBox" in event.WindowName:
        
        left_click += 1

        if (left_click == 1):
            print "Please click top left of desired window"
        elif (left_click == 2): # if second click store top left position
            print "Please click bottom right of desired window"
            tlp_x = x
            tlp_y = y
        elif (left_click == 3): # if third click then store bottom right position
            brp_x = x
            brp_y = y
            print "Finished - Window size:", (brp_x-tlp_x), (brp_y-tlp_y)

        # if calibration is complete        
        if left_click > 3:

            # now calculate the position
            # the full range of the 3M microtouch range in hexadecimal mode is 000 - 3FF
            # use the full range (0 - 1023)
            
            screen_x = int ((1023.0 / (brp_x - tlp_x)) * (x - tlp_x))
            screen_y = int (((1023.0 / (brp_y - tlp_y)) * ((brp_y - tlp_y)-(y - tlp_y))))

            # set upper and lower limits on values if outside of defined area
            screen_x = set_limits(screen_x,0,1023)
            screen_y = set_limits(screen_y,0,1023)

            print "Sending (",screen_x,",",screen_y, ") Hex: (\x01"+format(screen_x, '03X')+","+format(screen_y, '03X')+"\x0d) "
            ser.write("\x01"+format(screen_x, '03X')+","+format(screen_y, '03X')+"\x0d")      # write a string

    else:
        print "Clicked outside DOSBox;"

    # return True to pass the event to other handlers
    # return False to stop the event from propagating
    return True

  
if __name__ == '__main__':

    # attempt to open serial port with parameters as specified
    try: 
        ser = serial.Serial(
            port='COM1',
            baudrate=9600,
            bytesize=7,
            parity='N',
            stopbits=2,
            timeout=0,
            xonxoff=True,
            rtscts=0
            )
        
        if ser.isOpen():

            # mouse stuff            
            
            hm = pyHook.HookManager()               # create the hook mananger           
            hm.MouseAllButtonsDown = OnMouseEvent   # register two callbacks            
            hm.HookMouse()                          # hook into the mouse and keyboard events

            # serial stuff
            
            ser.flushInput()                        # flush input buffer
            ser.flushOutput()                       # flush output buffer
            
            while True:
                pythoncom.PumpWaitingMessages() 

                # read any new lines
                response = ser.readline() # this is non-blocking, provided that timeout is set to 0
                
                # if there is a new line
                if response:

                    print (response)
                    
                    if "R" in response:
                        print "Reset Command"
                        ser.write('\x010\x0d')
                    elif "MS" in response:
                        print "Mode Stream"
                        ser.write('\x010\x0d')
                    elif "FH" in response:
                        print "Format Hex"
                        ser.write('\x010\x0d')
                    elif "MP" in response:
                        print "Mode Point"
                        ser.write('\x010\x0d')
                    elif "CI" in response:
                        print "Calibrate Interactive"
                        ser.write('\x010\x0d')  
                                        
            ser.close()    

    except serial.SerialException as e:
            print("Failed to open Serial Port: {}".format(e))

Please note that this has been tailored to the application that I’m running – but can be easily tweaked / amended as necessary using the microtouch reference guide. The shell output:

microtouch-emulator-output

The DOS application running  – with some numbers entered through the emulator using a mouse.

DOSBox-Output

One thought on “3M Microtouch touchscreen emulator in Python”

  1. Which version of Python are you using on Windows? ActiveState no longer seems to have 2.7.6 and i am having a hard time getting pyHook and such to run. What was the environment you used this script in?

Leave a Reply to Asure Cancel reply

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.