The SCalable Reference Architecture for a Tactile display Control Hardware
SCRATCHy is an open hardware- and software-plattform for controlling so-called "tactile Displays", i. e. devices that are intended to stimulate the touch sensitive receptors in human skin. In combination with ITCHy, the tactile mouse, this project forms a complete research environment for e. g. developing new models controlling tactile displays or for conducting user studies.
This repository contains the following parts that reflect the folder structure:
./pcb/
→ Schematics and layouts for printed circuit boards./libSCRATCHy/
→ Library to access the Signal Generators and peripherals./SCRATCHPy/
→ Python wrapper covering the high-level functionality of libSCRATCHy./examplesCpp/
→ Example programs using the C++ interface./examplesPython/
→ Example programs using the Python interface./generatorFirmware/
→ Signal Generator "operating system"
The circuit boards located in the ./pcb/
folder have been designed with Fritzing, an open source EDA software. For convenience, production ready Extended Gerber files can be found in their respective subfolders.
Since most of the parts on the PCBs come in SMD packages, some experience in soldering such small parts is beneficial. However, the smallest components used here (0805 Package) can still be soldered by hand easily. (There are some nice tutorial videos on how to solder SMD components by hand.)
Each of the PCB subfolders contains a Bill Of Material (BOM) listing the components required for a single board. The part numbers given in these lists can be looked up on Farnell/Newark.
The hardware system consists of a single main processor and several so-called Signal Generators (SGs) that are connected to a custom SPI/I²C bus. Each of these SGs is able to generate up to 4 indepentent analog signals. Depending of the type of SG, these signals can be amplified externally or are amplified onboard to up to ±200 V (theoretically, has been tested with 120V).
The whole system builds upon the MainComputeUnit (MCU) that hosts a Raspberri Pi 3. This linux based computer is intended to generate - depending on e.g. position and velocity of the Tactile Mouse - a rather abstract signal specification for each actuator of the tactile display that is distributed across the SPI bus. Finally, the connected SGs generate a continuous waveform that can be further amplified or processed.
The PCB exposes various GPIO pins as well as SPI and I²C ports of the Raspberry Pi 3 to a common data-bus. Additionally, the DisplayBreaktout board can be connected using a separate plug. This PCB also acts as a pedestal for other modules that can simply be stacked on top. It can be fixated with four M3 screws.
This optional PCB hosts a small graphical OLED display as well as some tactile switches that can be accessed via libSCRATCHy
. It is connected to the MCU via I²C (and some GPIO pins used for the buttons). Since the Raspberry Pi 3 can connect via HDMI to an external monitor, this board is completely optional.
The AnalogSignalGenerator (ASG) contains two Signal Generators within one PCB and is therefore able to generate 8 analog output signals simultaneously. The control data sent from the MCU is interpreted using a custom software running on a Teensy 3.2 USB development board and the resulting time-varying waveform is generated using PWM. This signal is then lowpass filtered to the frequency range relevant to the mechanoreceptors (0-1000 Hz).
The HighVoltageAmplifier (HVA) contains a single Signal Generator as well as a series of Class-D amplifiers in H bridge topology. Using a separate power supply (ranging from 0 to 200V max.) the PWM signals generated by the Teensy 3.2 are amplified to amplitudes of up to 200V (peak to peak, max.). In combination with a separate passive low pass filter, this signal may be used to drive e. g. piezoelectric actuators directly.
Working with such high voltages is dangerous! A sufficient casing is needed to prevent accidential contact. Use at your own risk! This device has been tested for voltages up to 120 V. Depending on load and voltage, active cooling may be needed.
This board is needed for low-pass filtering the high voltage signals coming from the HVA. It contains several inductors that form in combination with the capacitivity of the piezoelectric actuators a sufficient filter.
Before the Teensy can be flashed, please install Teensyduino by following the official instructions. Afterwards, building and flashing the firmware is straightforward:
cd generatorFirmware
make
If the build process was successfull, you can install the firmware by connecting the Teensy (separate it from the SG board before!) via USB and running
make upload
libSCRATCHy requires the following dependencies to be installed:
If the Raspberry Pi 3 is going to be used for the MCU (recommended), the following libraries need to be installed as well:
SCRATCHPy, the python wrapper for libSCRATCHy, does need two more dependencies:
- Python 3.6m
- Boost.Python
libSCRATCHy uses the qmake build system and is available for linux platforms only. For building and installing the library system-wide, please follow these steps within the base directory:
mkdir libSCRATCHy-build
cd libSCRATCHy-build
qmake ../libSCRATCHy
make && sudo make install
If you intend to use libSCRATCHy on a personal computer (not using the Raspberry Pi 3), you may pass an additional configuration option to qmake that disables some platform specific tests at runtime
[...]
qmake ../libSCRATCHy CONFIG+=fake
make && sudo make install
This also causes the iowrap_dummy.cpp
to be built instead of iowrap_raspberry.cpp
. By using fake mode, one can make use of the complete SCRATCHy API without triggering any hardware specific actions (e.g. GPIO access, I²C communication, etc.).
The directory examplesCpp
contains a minimal example project that can be build using qmake:
mkdir minimalExample-build
cd minimalExample-build
qmake ../examplesCpp/minimalExample
make
./MinimalExample
Please make sure that libSCRATCHy as well as libITCHy has been build and installed beforehand. This application will output a frequency that is scaled with the current movement speed of the tactile mouse.
A python-based application equivalent to the C++ example can be found in the examplesPython
directory.
The python script minimalExample.py
needs the python bindings of libSCRATCHy and libITCHy to be build.
When running
python minimalExample.py
the application should do exactly the same as the C++ example.
In case there are ModuleNotFoundError
messages, please adjust the lines
sys.path.append(os.path.abspath('../../SCRATCHPy/build/lib.linux-x86_64-3.6/'))
sys.path.append(os.path.abspath('../../../ITCHy/ITCHPy/build/lib.linux-x86_64-3.6/'))
to match the actual build-path of the libraries.
The SCRACTHy API is separated into two categories: High- and Lowlevel functionality. In the majority of cases, the Highlevel API should be sufficient to control the complete system. In case custom devices have been added to the bus-system, or if the SignalGenerators following a non-standard protocol, the Lowlevel API allows for direct access to GPIO, SPI and I²C.
This class allows accessing the (optional) DisplayBreakout by providing convenience functions for displaying icons and text.
Separates the GraphicalDisplay from the main thread, causing the show
and text
methods to be non-blocking.
However, special attention has to be drawn when communication via I²C from a different thread simultaneously.
Queries if a specific button is pressed at the moment of the call.
Button
may be one of the following values:
Button::Back
Button::Up
Button::Down
Button::Select
(The rightmost button on the DisplayBreakout)
Clears the OLED Display causing all pixels to be set to black.
Renders a default arrangement to the OLED-display consisting of a small 32x32px pictogram on the left, a header text in a slightly larger font and a body text with the default 6x6px font. In case the strings are too long to be displayed they will be truncated.
The following pictograms can be selected:
Icon::None
, Icon::Scratchy
, Icon::Spectrum
, Icon::Forbidden
Icon::Bolt
, Icon::Bug
, Icon::Check
, Icon::Clock
Icon::Cog
, Icon::Fire
, Icon::Image
, Icon::Reboot
Icon::Power
, Icon::Pulse
, Icon::Random
, Icon::Warning
This is an overloaded method for quickly showing numerical values without the need for explicit string conversion.
Displays the specified text on the OLED-display. In case the string is too long to be displayed in one line (21 characters) it will be wrapped automatically.
The SignalManager
is responsible for initializing the individual SignalBoards correctly. It automatically enumerates all connected SignalBoards, assigns the correct I²C addresses and gives access to individual boards. For debugging purposes, error messages and general information will be printed to std::cerr
.
Excludes a specific I²C address from the enumeration process. This method can be called multiple times in order to mask more than one address. It can be used to permanently deactivate specific SignalBoards or in case additional I²C periphery has been added (e.g. the DisplayBreakout that will occupy the I²C address 0x3C
).
Resets all SignalBoards connected to the system and runs a "standard procedure" consisting of the following steps:
- (global Reset)
- Initialization of connected Signalboards
- Assignment of I²C addresses according to the selected hardware address
- Passing through the different runlevels of the SignalBoards and configuring the DAC resolution and sampling rate
- Initiating the "running" state on all SignalBoards
The samplingTime
is given in micro seconds and defaults to 62 µs resulting in a sampling rate of approximately 1/(62 µs) = 16 kHz.
Iterates over the whole (non masked) I²C address range and asks for an acknowledgment to the SystemRequest::Alive
request. All properly initialized SignalBoards that respond to this request will be included in the returned list of I²C addresses.
Returns a list of all SignalGenerators that have been successfully enumerated.
Returns a list of I²C addresses representing all active SignalBoards.
Returns the SignalGenerator
instance that maps to the specified address.
Warning: Currently there are no checks if the address actually has been assigned.
Causes a specific SignalBoard to be resetted.
Warning: This method usually is called within the initializeBoards
method. In most use cases, you probably do not want to call it manually.
Causes all (non masked) SignalBoard connected to the system to be resetted.
Warning: This method usually is called within the initializeBoards
method. In most use cases, you probably do not want to call it manually.
Initializes all SignalBoards that have been resetted beforehand, causing them to enter Runlevel 0
Warning: This method usually is called within the initializeBoards
method. In most use cases, you probably do not want to call it manually.
Iterates through all possible SignalGenerator addresses and assigns a new I²C address where applicable. Causes all SignalGenerator
instances to be deleted an re-instanciated. Please make sure, the SignalGenerators have been resetted and initialized before this method is called.
Warning: This method usually is called within the initializeBoards
method. In most use cases, you probably do not want to call it manually.
All lowlevel functions can be accessed via the iowrap.h
header file. The lowlevel API gives access to platformspecific functionality such as GPIO configuration, direct SPI communication as well as sending custom I²C commands to the SignalBoards.