تاچپد لمسی با پرینت سهبعدی و برد ESP32
تاچپدها و صفحات لمسی پای ثابت دستگاههای امروزی هستن، ولی ساختنشون از صفر همیشه یه چالش بزرگ به نظر میرسه. خبر خوب اینه که با پیشرفت پرینترهای سهبعدی و فیلامنتهای رسانا، دیگه نیازی به بردهای پیچیده شرکتی نداریم و میتونیم سطح لمسی اختصاصی خودمون رو طراحی کنیم.
تو این آموزش از آیسیمدار قراره با استفاده از فیلامنت رسانا و برد XIAO ESP32-C6 (یا هر میکروکنترلر سازگار با آردوینو)، یه تاچپد سنسوری جذاب بسازیم. ایده کار خیلی سادهست؛ ما یه شبکه از خطوط رسانا رو روی یه طلق شفاف پرینت میکنیم و وقتی انگشتمون رو روی این خطوط میکشیم، مقاومت الکتریکی تغییر میکنه. میکروکنترلر این تغییرات رو میخونه و مختصات دقیق لمس رو روی یه نمایشگر OLED نشون میده.
این پروژه ترکیب بینظیری از الکترونیک و پرینت سهبعدیه که میتونه پایه ساخت کلی گجت خلاقانه باشه. پس وسایلت رو آماده کن تا بریم سراغ طراحی و ساخت این تاچپد جالب.
قطعات موردنیاز
- فیلامنت رسانای پرینت سهبعدی (PLA رسانا)
- طلق شفاف پرینتر (مقاوم در برابر حرارت مخصوص پروژکتورها)
- برد XIAO ESP32-C6 (یا بردهای سازگار با آردوینو)
- نمایشگر OLED با رزولوشن 128 در 64 پیکسل
- سیمهای جامپر و هویه
طراحی مسیرهای رسانا
برای ساخت یه شبکه سنسوری راههای مختلفی وجود داره. اساس کار اینه که دو تا مسیر رسانا کنار هم قرار بگیرن و وقتی لمسشون میکنی، از طریق رطوبت دستت یه اتصال کوتاه بینشون برقرار بشه. میتونی از لایهها و مواد مختلفی استفاده کنی، اما ما میخوایم کل این شبکه رو با پرینتر سهبعدی بسازیم.
این سنسورها دقیقا با همین مکانیزم ساده کار میکنن؛ یعنی با لمس پوستت، اتصال الکتریکی بین دو تا سیم رسانا کامل میشه و برد متوجه این تغییر میشه.
حالا فقط کافیه تشخیص بدیم این تماس دقیقا کجای شبکه اتفاق افتاده تا بتونیم مختصاتش رو دربیاریم. تو عکسها دو تا طراحی مختلف رو میبینی. تو طرح اول، نیاز داری چند تا پل ارتباطی رو خودت به صورت دستی با تیکههای فیلامنت اضافه کنی. اما طرح دوم کلا با پرینتر سهبعدی زده میشه و نیازی به کار دستی نداره، فقط مسیرهای خطوطش کمی طولانیتره.
ابعاد و ضخامت خطوط روی برد
برای اینکه تاچپدمون خیلی بزرگ و جاگیر نشه، عرض مسیرهای رسانا رو 1 میلیمتر در نظر گرفتم. حواست باشه که فاصله بین این خطوط هم خیلی مهمه و نباید به هم بچسبن. بعد از کلی تست و بررسی، به این نتیجه رسیدم که بهترین ضخامت برای لایههای پرینتشده 0.4 میلیمتره.
البته میتونی از ضخامت 0.2 میلیمتر هم استفاده کنی، ولی باید بدونی که تو این حالت مقاومت الکتریکی مسیرها بالاتر میره و ممکنه دقت تاچپد کمی افت کنه.
تنظیمات طلایی برای پرینت سهبعدی
برای پرینت این شبکه رسانا باید یه سطح پایه مناسب انتخاب کنی تا فیلامنت به خوبی روش بشینه. اول روی چوب تست کردم تا ببینم جواب میده یا نه. چوب مقاومت خوبی داره، اما چون بافتش متخلخله، مقداری از فیلامنت رو به خودش جذب میکنه و سطح کار حسابی ناهموار و زشت میشه.
بهترین گزینه، استفاده از طلقهای شفاف و نسوز مخصوص پروژکتوره. این طلقها هم استحکام خوبی دارن و هم انعطافپذیرن. فقط کافیه طلق رو با نوار چسب روی صفحه پرینترت محکم کنی.
بسته به نوع پرینترت، شاید نیاز باشه یه آفست (Offset) کوچیک برای محور Z تعریف کنی تا نازل به طلق کشیده نشه؛ پیشنهاد من اینه که مقدارش رو روی 0.1 میلیمتر تنظیم کنی.
یه نکته خیلی مهم: دمای صفحه کار (Bed) رو روی حالت ولرم یعنی حدود 30 درجه سانتیگراد تنظیم کن. دمای نازل رو روی 250 درجه، سرعت پرینت رو روی 25 میلیمتر بر ثانیه و آفست محور Z رو هم روی همون 0.1 میلیمتر بذار تا یه خروجی بینقص بگیری.
اندازهگیری مقاومت الکتریکی مسیرها
بعد از اینکه پرینت تموم شد، باید یه مولتیمتر برداری و مقاومت الکتریکی رو تو گوشه بالا سمت چپ و گوشه پایین سمت راست شبکه اندازه بگیری. این دو تا عدد رو یادداشت کن، چون باید تو کدهای برنامه به جای متغیرهای resMin و resMax قرارشون بدی.
کار اصلی رو میکروکنترلر انجام میده؛ یعنی مقاومت نقطهای که لمس کردی رو میخونه، اون رو به یه مختصات دقیق روی شبکه سنسور تبدیل میکنه و در نهایت روی نمایشگر OLED نشون میده.
تبدیل دادهها به مختصات دقیق در کدهای آردوینو
برنامهای که نوشتیم، اختلاف بین کمترین و بیشترین مقاومت رو به 7 سطر (برای محور Y) تقسیم میکنه. اینطوری هر مقداری که میکروکنترلر بخونه، به راحتی به یه سطر خاص اختصاص داده میشه. تو هر سطر هم، با استفاده از دستور map، مقدار مقاومت به یه ستون یا همون محور X (از 1 تا 127 که عرض نمایشگره) تبدیل میشه.
من برنامه رو طوری نوشتم که نقطه لمسشده روی نمایشگر باقی بمونه تا وقتی که یه نقطه جدید رو لمس کنی. البته اگه بخوای، میتونی کد رو تغییر بدی تا وقتی دستت رو برمیداری، اگر سیگنالی نبود نقطه هم از روی صفحه پاک بشه.
میکروکنترلر ما هر 10 میلیثانیه یکبار مقاومت رو چک میکنه و دادهها رو درست مثل سیستم نمایشگر OLED، از گوشه بالا سمت چپ میخونه و پردازش میکنه.
کدهای برنامه و راهاندازی نهایی مدار
// DIY sensor touchpad
// Markus Opitz 2026
// instructables.com
#include <Arduino.h>
// measure ***********************
const int ADC_PIN = A0;
const float VCC = 3.3;
const float R_REF = 1000000.0; // 1 MOhm reference resistor
const int N_SAMPLES = 64;
//coordinates **************
float resMin = 2.0;
float resMax = 6.0;
float resLine;
float resValue;
int x, y;
float resX;
// OLED ******************
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_ADDR 0x3D // I2C-address (mostly 0x3C)
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
float mapFloat(float x, float in_min, float in_max, float out_min, float out_max) { //for using float values in a map funktion
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
void setup() {
Serial.begin(115200);
delay(500);
resLine = (resMax-resMin)/7; // 7 lines --> a 9 lines on OLED display
Serial.println(resLine, 3); delay(2000);
pinMode(ADC_PIN, INPUT);
analogReadResolution(12);
analogSetPinAttenuation(ADC_PIN, ADC_11db); // großer Messbereich bis nahe 3.3 V
if (!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDR)) {
Serial.println(F("SSD1306 not found"));
for (;;);
}
display.clearDisplay();
display.setTextSize(2);
display.setTextColor(WHITE);
display.setCursor(30, 25);
display.println(F("ready"));
display.display();
delay(1000);
}
void loop() {
uint32_t sum = 0;
for (int i = 0; i < N_SAMPLES; i++) {
sum += analogReadMilliVolts(ADC_PIN);
delayMicroseconds(200);
}
float vOut = (sum / (float)N_SAMPLES) / 1000.0;
float rX;
if (vOut <= 0.001) {
rX = 0.0;
} else if (vOut >= VCC - 0.001) {
rX = INFINITY;
} else {
rX = R_REF * vOut / (VCC - vOut);
}
Serial.print("Vout = ");
Serial.print(vOut, 3);
Serial.print(" V, Rx = ");
resValue = rX / 1000000.0;
if (isinf(rX)) {
Serial.println("> Messbereich / sehr hochohmig");
}
else if (resValue > 75) Serial.println(" - ");
else {
Serial.print(resValue, 3);
Serial.print(" MOhm ");
checkXY();
Serial.print(x); Serial.print(" "); Serial.println(y);
displayDot();
}
delay(500);
}
void checkXY() {
if ((resValue > resMin) && (resValue < (resMin + resLine))) {
y=5;
//resX = resValue;
//x = mapFloat(resX, resMin,(resMin+ resLine) , 1, 128);
x = (int)mapFloat(resValue, resMin, (resMin+ resLine), 1, 126);
//Serial.println("line 1");
}
if ((resValue > resMin + resLine) && (resValue < (resMin + 2 * resLine))) {
y=14;
resX = resValue;
//x = mapFloat(resX, (resMin + 1*resLine),(resMin+ 2*resLine) , 128, 1);
x = (int)mapFloat(resValue, (resMin + 1*resLine), (resMin+ 2*resLine), 126, 1);
//Serial.println("line 2");
}
if ((resValue > resMin + 2 * resLine) && (resValue < (resMin + 3 * resLine))) {
y=23;
resX = resValue;
//x = mapFloat(resX, (resMin + 2*resLine),(resMin+ 3*resLine) , 1, 128);
x = (int)mapFloat(resValue, (resMin + 2*resLine), (resMin+ 3*resLine), 1, 126);
//Serial.println("line 3");
}
if ((resValue > resMin + 3 * resLine) && (resValue < (resMin + 4 * resLine))) {
y=32;
resX = resValue;
//x = mapFloat(resX, (resMin + 3*resLine),(resMin+ 4*resLine) , 128, 1);
x = (int)mapFloat(resValue, (resMin + 3*resLine), (resMin+ 4*resLine), 126, 1);
//Serial.println("line 4");
}
if ((resValue > resMin + 4 * resLine) && (resValue < (resMin + 5 * resLine))) {
y=41;
resX = resValue;
//x = mapFloat(resX, (resMin + 4*resLine),(resMin+ 5*resLine) , 1, 128);
x = (int)mapFloat(resValue, (resMin + 4*resLine), (resMin+ 5*resLine), 1, 126);
//Serial.println("line 5");
}
if ((resValue > resMin + 5 * resLine) && (resValue < (resMin + 6 * resLine))) {
y=50;
resX = resValue;
//x = mapFloat(resX, (resMin + 5*resLine),(resMin+ 6*resLine) , 128, 1);
x = (int)mapFloat(resValue, (resMin + 5*resLine), (resMin+ 6*resLine), 126, 1);
//Serial.println("line 6");
}
if ((resValue > resMin + 6 * resLine) && (resValue < (resMax))) {
y=59;
resX = resValue;
//x = mapFloat(resX, (resMin+6*resLine),(resMax) , 1, 128);
x = (int)mapFloat(resValue, (resMin + 6*resLine), resMax, 1, 126);
//Serial.println("line 7");
}
}
void displayDot() {
display.clearDisplay();
display.fillCircle(x, y, 2, SSD1306_WHITE);
display.display();
delay(10);
}
فایلهای پیوست
تو میتونی اولین سازنده باشی!
اگه پروژه رو ساختی به اشتراک بزار و اعتبار هدیه بگیر
این آموزش با استانداردهای اختصاصی آیسیمدار بازنویسی و بهینهسازی شده و تمامی حقوق انتشار آن متعلق به این مجموعه است و در صورت کپی یا بازنشر پیگرد قانونی خواهد داشت.








































گفتگوهای کاربران
هنوز نظری ثبت نشده؛ تو شروع کن!
هنوز نظری ثبت نشده؛ تو شروع کن!