Capacitive touch pad auto-translator
Used to generate code from GPT-5
// XIAO RP2040 Morse key (capacitive pad) with auto-translation
// Pad: bottom-right -> GPIO4 (your "D9")
// LED: onboard green -> GPIO16
// Open Serial Monitor @ 115200
// --------- Pins ---------
const int TOUCH_PIN = 4; // <-- GPIO for your pad (bottom-right)
const int LED_PIN = 16; // onboard green LED
// --------- Morse timing (tweakable) ---------
const unsigned long DOT_MS = 250; // base unit
const unsigned long DASH_MS = DOT_MS * 3; // duration threshold
const unsigned long LETTER_GAP = DOT_MS * 3; // pause between letters
const unsigned long WORD_GAP = DOT_MS * 7; // pause between words
// --------- Capacitive read settings ---------
const unsigned long TIMEOUT = 40000; // larger => more range
const int DISCHARGE_US = 200; // ensure pad discharges
const int SAMPLES = 3;
const float FILTER_ALPHA = 0.30f;
const int TOUCH_MARGIN = 80; // above baseline = "touched"
unsigned long baseline = 0;
float filt = 0.0f;
bool isTouched = false;
// --------- Morse table ---------
struct MorseMap { const char* code; char ch; };
const MorseMap MORSE[] = {
{".-", 'A'}, {"-...", 'B'}, {"-.-.", 'C'}, {"-..", 'D'}, {".", 'E'},
{"..-.", 'F'}, {"--.", 'G'}, {"....", 'H'}, {"..", 'I'}, {".---", 'J'},
{"-.-", 'K'}, {".-..", 'L'} {"--", 'M'}, {"-.", 'N'}, {"---", 'O'},
{".--.", 'P'}, {"--.-", 'Q'}, {".-.", 'R'}, {"...", 'S'}, {"-", 'T'},
{"..-", 'U'}, {"...-", 'V'}, {".--", 'W'}, {"-..-", 'X'}, {"-.--", 'Y'},
{"--..", 'Z'},
{"-----",'0'}, {".----",'1'}, {"..---",'2'}, {"...--",'3'}, {"....-",'4'},
{".....",'5'}, {"-....",'6'}, {"--...",'7'}, {"---..",'8'}, {"----.",'9'},
{NULL, 0}
};
// --------- Capacitive helper ---------
unsigned long touchReadCTM(int pin) {
pinMode(pin, OUTPUT);
digitalWrite(pin, LOW);
delayMicroseconds(DISCHARGE_US);
pinMode(pin, INPUT_PULLUP);
unsigned long c = 0;
while (digitalRead(pin) == LOW && c < TIMEOUT) c++;
return c;
}
unsigned long readAvg(int pin, int n=SAMPLES){
unsigned long s=0; for(int i=0;i<n;i++) s += touchReadCTM(pin);
return s / (unsigned long)n;
}
// --------- Keying state ---------
String currentCode = ""; // collects "." and "-" for current letter
unsigned long lastEdgeTime = 0; // last touch/release moment
bool keyDown = false; // current key state
char decodeLetter(const String& code) {
for (int i=0; MORSE[i].code; i++) {
if (code.equals(MORSE[i].code)) return MORSE[i].ch;
}
return '?'; // unknown pattern
}
void emitLetterIfAny() {
if (currentCode.length() == 0) return;
char ch = decodeLetter(currentCode);
Serial.print(ch);
currentCode = "";
}
void setup() {
Serial.begin(115200);
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW);
// Baseline calibration (hands off the pad for a moment)
delay(300);
unsigned long sum=0;
for (int i=0;i<40;i++){ sum += touchReadCTM(TOUCH_PIN); delay(2); }
baseline = sum/40;
filt = baseline;
Serial.println("\nMorse key ready. Tap for dot, hold for dash.");
Serial.print("Baseline="); Serial.println(baseline);
}
void loop() {
// --- capacitive read + smoothing ---
unsigned long v = readAvg(TOUCH_PIN);
filt = FILTER_ALPHA * v + (1.0f - FILTER_ALPHA) * filt;
// hysteresis around baseline
static bool touched = false;
int onT = baseline + TOUCH_MARGIN;
int offT = baseline + TOUCH_MARGIN/2;
if (!touched && filt > onT) touched = true;
if (touched && filt < offT) touched = false;
// --- edge detection ---
unsigned long now = millis();
if (touched && !keyDown) {
// key press
keyDown = true;
lastEdgeTime = now;
digitalWrite(LED_PIN, HIGH);
} else if (!touched && keyDown) {
// key release: decide dot or dash
keyDown = false;
unsigned long pressDur = now - lastEdgeTime;
currentCode += (pressDur < (DASH_MS - DOT_MS)) ? "." : "-";
lastEdgeTime = now;
digitalWrite(LED_PIN, LOW);
}
// --- spacing logic (letter/word boundaries) ---
if (!keyDown) {
unsigned long idle = now - lastEdgeTime;
if (currentCode.length() > 0 && idle >= LETTER_GAP && idle < WORD_GAP) {
emitLetterIfAny(); // end of letter
Serial.flush();
} else if (idle >= WORD_GAP) {
emitLetterIfAny(); // end of word
Serial.print(' ');
Serial.flush();
lastEdgeTime = now; // avoid repeated spaces
}
}
delay(4);
}