Skip to content

Instantly share code, notes, and snippets.

@OverShifted
Created August 6, 2025 09:09
Show Gist options
  • Save OverShifted/1ca179eb386d6b3181de0bbd72e66122 to your computer and use it in GitHub Desktop.
Save OverShifted/1ca179eb386d6b3181de0bbd72e66122 to your computer and use it in GitHub Desktop.
A simple ESP32 paint program with the option of changing color with a rotary encoder
#include <TFT_eSPI.h>
#include <SPI.h>
#define OUTPUT_BTN 13 // SW
#define OUTPUT_A 18 // CLK
#define OUTPUT_B 17 // DT
TFT_eSPI tft = TFT_eSPI();
float h = 0.0, s = 0.0, v = 1.0;
uint currentChannel = 0;
const char *channelNames[3] = { "h", "s", "v" };
bool touchDownLastFrame = false;
int xLastFrame, yLastFrame;
bool buttonLastFrame = false;
int outputALastFrame;
int counter = 0;
void HSVtoRGB(float h, float s, float v, uint8_t &r, uint8_t &g, uint8_t &b) {
float c = v * s;
float x = c * (1 - abs(fmod(h / 60.0, 2) - 1));
float m = v - c;
float r1, g1, b1;
if (h < 60) { r1 = c; g1 = x; b1 = 0; }
else if (h < 120) { r1 = x; g1 = c; b1 = 0; }
else if (h < 180) { r1 = 0; g1 = c; b1 = x; }
else if (h < 240) { r1 = 0; g1 = x; b1 = c; }
else if (h < 300) { r1 = x; g1 = 0; b1 = c; }
else { r1 = c; g1 = 0; b1 = x; }
r = (r1 + m) * 255;
g = (g1 + m) * 255;
b = (b1 + m) * 255;
}
void setup() {
Serial.begin(115200);
tft.init();
tft.setRotation(0);
tft.fillScreen(TFT_BLACK);
pinMode(OUTPUT_BTN, INPUT_PULLUP);
pinMode(OUTPUT_A, INPUT_PULLDOWN);
pinMode(OUTPUT_B, INPUT_PULLDOWN);
outputALastFrame = digitalRead(OUTPUT_A);
}
void loop() {
uint8_t red, green, blue;
HSVtoRGB(h, s, v, red, green, blue);
uint16_t color = tft.color565(red, green, blue);
uint16_t tx, ty;
bool touchDown = tft.getTouch(&tx, &ty);
if (touchDown) {
// You might need manual callibration here:
int x = map(ty, 316, -5, 0, 240);
int y = map(tx, 245, 8, 0, 320);
if (touchDownLastFrame) {
tft.drawWideLine(xLastFrame, yLastFrame, x, y, 3, color, TFT_BLACK);
}
xLastFrame = x;
yLastFrame = y;
}
touchDownLastFrame = touchDown;
bool button = digitalRead(OUTPUT_BTN) == 0;
if (button && !buttonLastFrame)
{
currentChannel = (currentChannel + 1) % 3;
delay(50); // Debounce
}
buttonLastFrame = button;
float delta = 0;
int outputA = digitalRead(OUTPUT_A);
if (outputA != outputALastFrame) {
if (digitalRead(OUTPUT_B) != outputA)
delta = 1;
else
delta = -1;
}
outputALastFrame = outputA;
if (currentChannel == 0)
h += delta * 8;
else if (currentChannel == 1)
s += delta * 0.05;
else if (currentChannel == 2)
v += delta * 0.05;
if (h < 0) h += 360;
if (h > 360) h -= 360;
s = min(max(s, 0.0f), 1.0f);
v = min(max(v, 0.0f), 1.0f);
rgbLedWrite(RGB_BUILTIN, red / 40, green / 40, blue / 40);
tft.fillRect(5, 5, 10, 10, color);
tft.setTextColor(TFT_WHITE, TFT_BLACK);
tft.setTextSize(1.5);
tft.drawString(channelNames[currentChannel], 20, 5);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment