Touch paint on an Arduino Uno
Draw on a 2.8 inch TFT with its resistive touch panel: two SPI chips on one module, and the raw touch counts mapped to pixels. The whole build, an Arduino Uno and 1 more part, runs here in your browser on the firmware below; open it in the editor to change the wiring or the code and run it again.
IntermediateRuns in your browser. Free, and no account needed.
The code
The firmware exactly as the editor opens it. Change a line there and press Run: it compiles in the browser.
// Touch paint: draw on a 2.8 inch ILI9341 with its XPT2046 touch panel.
//
// display CS -> 10 DC -> 9 RST -> 8 MOSI -> 11 SCK -> 13 LED -> 5 V
// touch T_CS -> 7 T_CLK -> A2 T_DIN -> A0 T_DO -> A1
//
// Press on the picture while it runs, and drag.
//
// Two chips on one module, each an SPI device with its own chip select. They
// could share one bus, since each only listens while its own select is low;
// here the touch controller has a bus of its own on A0 to A2, an SPIClass
// made on those pins, so the two sets of wires never meet.
// Each reading is a control byte and sixteen clocks: the start bit, the
// channel, then twelve bits of result in bits 14 to 3 (the >> 3). Pressure is
// 4095 + Z1 - Z2, large for a press and near nothing for none.
//
// The readings are raw counts, about 200 at one edge of the picture and 3900
// at the other, so map() turns them into pixels. With the display and the
// touch at the same rotation, a bigger x is further right.
//
// The row of colours along the top is the palette: touch one to pick it.
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <XPT2046_Touchscreen.h>
Adafruit_ILI9341 tft(10, 9, 8);
SPIClass touchBus(A2, A1, A0, 7); // SCK, MISO, MOSI, SS
XPT2046_Touchscreen ts(7);
const uint16_t PALETTE[] = {ILI9341_RED, ILI9341_YELLOW, ILI9341_GREEN,
ILI9341_CYAN, ILI9341_BLUE, ILI9341_WHITE};
const int SWATCH = 40;
uint16_t ink = ILI9341_YELLOW;
void setup() {
tft.begin();
tft.setRotation(1);
tft.fillScreen(ILI9341_BLACK);
for (int i = 0; i < 6; i++) tft.fillRect(i * SWATCH, 0, SWATCH, 20, PALETTE[i]);
ts.begin(touchBus);
ts.setRotation(1);
}
void loop() {
if (!ts.touched()) return;
TS_Point p = ts.getPoint();
int x = map(p.x, 200, 3900, 0, tft.width());
int y = map(p.y, 200, 3900, 0, tft.height());
if (y < 20) {
if (x >= 0 && x < 6 * SWATCH) ink = PALETTE[x / SWATCH];
return;
}
tft.fillRect(x - 2, y - 2, 5, 5, ink);
}
Parts list
3 parts, plus the jumper wires. Every one is in the editor's parts bin.
How it is wired
11 connections, pin by pin, read from the circuit itself. Each line is one set of pins joined together, by a jumper wire or a breadboard strip.
- Arduino Uno R3 pin 13; Touch TFT, 2.8 inch ILI9341 with XPT2046 pin SCK
- Arduino Uno R3 pin 11; Touch TFT, 2.8 inch ILI9341 with XPT2046 pin MOSI
- Arduino Uno R3 pin 10; Touch TFT, 2.8 inch ILI9341 with XPT2046 pin CS
- Arduino Uno R3 pin 9; Touch TFT, 2.8 inch ILI9341 with XPT2046 pin DC
- Arduino Uno R3 pin 8; Touch TFT, 2.8 inch ILI9341 with XPT2046 pin RST
- Arduino Uno R3 pin 7; Touch TFT, 2.8 inch ILI9341 with XPT2046 pin T_CS
- Arduino Uno R3 pin 5V; Touch TFT, 2.8 inch ILI9341 with XPT2046 pin VCC; Touch TFT, 2.8 inch ILI9341 with XPT2046 pin LED
- Ground: Arduino Uno R3 pin GND; Touch TFT, 2.8 inch ILI9341 with XPT2046 pin GND
- Arduino Uno R3 pin A0; Touch TFT, 2.8 inch ILI9341 with XPT2046 pin T_DIN
- Arduino Uno R3 pin A1; Touch TFT, 2.8 inch ILI9341 with XPT2046 pin T_DO
- Arduino Uno R3 pin A2; Touch TFT, 2.8 inch ILI9341 with XPT2046 pin T_CLK
Change it and keep it
Open it in the editor, change the circuit or the code, and keep your version in a free account.