🎛️ analogRead در ESP32: خواندن پتانسیومتر با ADC دوازدهبیتی و تبدیل به ولتاژ
در آردوینو اونو analogRead عددی بین ۰ تا ۱۰۲۳ برمیگرداند و تقریباً همیشه همانطور که انتظار داری کار میکند. در ESP32 همان دستور را مینویسی، ولی سه چیز فرق دارد: بازهی عدد بزرگتر است، ولتاژ مرجع ۳٫۳ ولت است و نیمی از پایههای آنالوگ با روشن شدن وایفای از کار میافتند. در این آموزش analogRead در ESP32 را با یک پتانسیومتر یاد میگیریم: عدد خام را میخوانیم، نویزش را کم میکنیم، به میلیولت و درصد تبدیلش میکنیم و با آن یک LED را با «هیسترزیس» روشن و خاموش میکنیم تا در مرز چشمک نزند. همهچیز را در شبیهساز ESP32، به همان سبکی که در ووکوی (Wokwi) کار میکنی، امتحان میکنیم.
ADC ۱۲ بیتی یعنی چه؟
ADC یا مبدل آنالوگ به دیجیتال، ولتاژ یک پایه را به یک عدد تبدیل میکند. «۱۲ بیتی» یعنی این عدد میتواند ۲ به توان ۱۲، یعنی ۴۰۹۶ مقدار مختلف داشته باشد: از ۰ تا ۴۰۹۵. صفر یعنی صفر ولت و ۴۰۹۵ یعنی تقریباً ۳٫۳ ولت. پس هر واحد حدود ۰٫۸ میلیولت است؛ چهار برابر دقیقتر از اونو. البته این دقت روی کاغذ است. ADC در ESP32 کمی نویز دارد و در دو سر بازه خطی نیست: زیر حدود ۰٫۱ ولت معمولاً صفر میخواند و بالای حدود ۳٫۱ ولت به ۴۰۹۵ میچسبد. بهترین دقت در بازهی میانی است.
قطعهها و سیمکشی
- یک برد ESP32
- یک پتانسیومتر ۱۰ کیلو اهم
- LED آبی روی خود برد (GPIO2)؛ سیم لازم ندارد
| پایهی پتانسیومتر | به ESP32 | توضیح |
|---|---|---|
| پایهی کناری اول | 3V3 | نه VIN! بیشتر از ۳٫۳ ولت به پایهی آنالوگ نرسان |
| پایهی وسط (سرِ متحرک) | GPIO34 | روی ADC1 است و با وایفای هم کار میکند |
| پایهی کناری دوم | GND | زمین مشترک |
چرا GPIO34؟ چون روی ADC1 است، فقط ورودی است و هیچ نقشی در بوت برد ندارد. فهرست کامل پایههای آنالوگ و دلیل هر انتخاب را در پین اوت ESP32 نوشتهایم.
کد کامل
// ESP32: read a potentiometer with the 12-bit ADC.
const int POT = 34; // ADC1 pin, input-only: keeps working with Wi-Fi on
const int LED = 2; // built-in blue LED
const int SAMPLES = 16; // average a few readings to calm the noise
bool ledOn = false;
int readSmooth(int pin) {
long sum = 0;
for (int i = 0; i < SAMPLES; i++) {
sum += analogRead(pin);
delayMicroseconds(200);
}
return sum / SAMPLES;
}
void setup() {
Serial.begin(115200);
pinMode(LED, OUTPUT);
analogReadResolution(12); // 0..4095 (the ESP32 default)
analogSetAttenuation(ADC_11db); // full range, roughly 0..3.3 V
}
void loop() {
int raw = readSmooth(POT);
int mV = raw * 3300.0 / 4095; // simple conversion to millivolts
int percent = map(raw, 0, 4095, 0, 100);
// hysteresis: ON above 55 %, OFF below 45 %, so the LED never flickers at the edge
if (!ledOn && percent > 55) ledOn = true;
if (ledOn && percent < 45) ledOn = false;
digitalWrite(LED, ledOn ? HIGH : LOW);
// "name:value" pairs, ready for the serial plotter
Serial.print("raw:");
Serial.print(raw);
Serial.print(",mV:");
Serial.println(mV);
delay(100);
}
توضیح خط به خط
readSmoothبهجای یک بار، ۱۶ بار پشت سر هم میخواند و میانگین میگیرد. نویز تصادفی در میانگین تا حد زیادی خنثی میشود و عدد آرامتر میشود.delayMicroseconds(200)فاصلهی کوچکی بین نمونهها میگذارد.analogReadResolution(12)دقت را ۱۲ بیت نگه میدارد. اگر کدی از اونو آوردهای که با ۰ تا ۱۰۲۳ حساب میکند، میتوانی اینجا10بنویسی تا بقیهی کد دست نخورد.analogSetAttenuation(ADC_11db)«تضعیفکننده» را روی بیشترین حالت میگذارد تا بازهی ورودی تا حدود ۳٫۳ ولت برسد. این حالت پیشفرض است. با حالتهای کوچکتر (ADC_0db،ADC_2_5db،ADC_6db) بازه کوچکتر و دقت در ولتاژهای پایین بیشتر میشود؛ مثلاً برای سنسوری که حداکثر یک ولت خروجی دارد.raw * 3300.0 / 4095عدد خام را به میلیولت تبدیل میکند. «.0» حساب را اعشاری میکند تا تقسیم گرد نشود، و نتیجه هنگام ریختن درintبه عدد صحیح تبدیل میشود. همین حساب را با یک کلیک در ماشینحساب map هم میتوانی چک کنی.map(raw, 0, 4095, 0, 100)عدد را به درصد تبدیل میکند. در اونو همین را با ۱۰۲۳ مینوشتی؛ یکی از رایجترین باگهای انتقال کد به ESP32 جا انداختن همین عدد است.- دو خط هیسترزیس قلب این برنامهاند. اگر فقط مینوشتیم «بالای ۵۰ روشن، زیرش خاموش»، وقتی ولوم دقیقاً روی ۵۰ درصد است، نویز عدد را ۴۹ و ۵۱ میکرد و LED دیوانهوار چشمک میزد. با دو آستانهی جدا، LED فقط با تغییر واقعی وضعیتش را عوض میکند. ترموستاتها و آبیاری خودکار هم دقیقاً همینطور کار میکنند.
- خروجی سریال به شکل
raw:…,mV:…است تا نمودار سریال دو خط زنده بکشد. چون هر دو عدد در بازهی چند هزار هستند، روی یک نمودار خوب کنار هم دیده میشوند.
analogReadMilliVolts: ولتاژ دقیقتر روی برد واقعی
فرمول بالا فرض میکند ADC کاملاً خطی است، ولی نیست. هر تراشهی ESP32 در کارخانه کالیبره میشود و اطلاعات کالیبراسیونش داخل خود تراشه ذخیره است. هستهی آردوینوی ESP32 دستوری دارد که از همین اطلاعات استفاده میکند و مستقیم میلیولت برمیگرداند: int mV = analogReadMilliVolts(34);. روی برد واقعی این عدد معمولاً چند ده میلیولت به ولتمتر نزدیکتر از فرمول ساده است. این دستور را روی برد واقعی امتحان کن و در شبیهساز از همان فرمول ساده استفاده کن.
ADC2 و وایفای: مهمترین تله
ESP32 دو ADC دارد. ADC1 روی پایههای 32 تا 39 است و ADC2 روی 0، 2، 4، 12 تا 15 و 25 تا 27. مشکل این است که رادیوی وایفای از ADC2 استفاده میکند. اگر پتانسیومتر را به GPIO4 یا GPIO25 وصل کنی، تا وقتی وایفای خاموش است همهچیز درست کار میکند؛ ولی به محض WiFi.begin، analogRead روی آن پایه عدد بیمعنی یا خطا برمیگرداند. خیلیها ساعتها دنبال باگ در کد وبسرورشان میگردند در حالی که مشکل فقط شمارهی پایه است. قانون ساده: در هر پروژهای که وایفای دارد، سنسور آنالوگ روی ADC1.
اجرا در شبیهساز
در تینکرکد فارسی برد ESP32 و پتانسیومتر را بگذار، سیمکشی جدول را انجام بده و کد را اجرا کن. با چرخاندن ولوم، عدد در سریال مانیتور از صفر تا ۴۰۹۵ تغییر میکند و LED آبی در ۵۵ درصد روشن و در ۴۵ درصد خاموش میشود. تب نمودار را باز کن تا دو منحنی را زنده ببینی. خواندن مثالها رایگان است و اجرای ESP32 به اشتراک Plus نیاز دارد. یک مثال آمادهی جذابتر هم هست: نمایش زندهی پتانسیومتر در صفحهی وب با ESP32؛ همین پتانسیومتر روی GPIO34، با وایفای روشن. اگر اصل analogRead را هنوز روی اونو یاد نگرفتهای، درس analogRead و پتانسیومتر رایگان است و معنی analogRead را هم در واژهنامه ببین.
اشکالهای رایج
- عدد همیشه ۴۰۹۵ است: پایهی وسط به 3V3 وصل شده یا پایهی GND پتانسیومتر شل است.
- عدد همیشه صفر است یا بیدلیل بالا و پایین میرود: سیم پایهی وسط به GPIO34 نرسیده و پایه معلق است.
- بعد از اضافه کردن وایفای خواندن خراب شد: سنسور روی ADC2 است؛ به 32 تا 39 منتقلش کن.
- درصد هیچوقت به ۱۰۰ نمیرسد: در
mapهنوز ۱۰۲۳ نوشتهای، یا ADC در انتهای بازه به ۴۰۹۵ نمیرسد. میتوانی بازهیmapرا مثلاً تا ۴۰۰۰ بگذاری و باconstrainمحدودش کنی. - ولتاژ نمایشدادهشده با ولتمتر نمیخواند: طبیعی است؛ روی برد واقعی
analogReadMilliVoltsرا جایگزین فرمول کن. - پتانسیومتر را به VIN بستهای: فوراً جدا کن. ۵ ولت روی پایهی ESP32 میتواند آن را بسوزاند.
حالا که عدد ولوم را داری، با آن چیزی را حرکت بده: کنترل سروو با ESP32 و پتانسیومتر، یا نور را با آن کم و زیاد کن: PWM با ledc. اگر پیکو داری، همین آموزش با میکروپایتون در ADC پیکو آمده است. همهی آموزشها در صفحهی ESP32.
پتانسیومتر و ESP32 را در شبیهساز امتحان کن 🎛️سؤالهای رایج
analogRead در ESP32 چه بازهای برمیگرداند؟
بهصورت پیشفرض عددی بین ۰ تا ۴۰۹۵، چون ADC در ESP32 دوازدهبیتی است. با analogReadResolution میتوانی آن را مثلاً به ۱۰ بیت (۰ تا ۱۰۲۳) تغییر دهی.
چرا عدد analogRead در ESP32 مدام بالا و پایین میرود؟
ADC در ESP32 نویز دارد. چند نمونه پشت سر هم بخوان و میانگین بگیر، یک خازن ۱۰۰ نانوفاراد بین پایه و GND بگذار و از تغییرات یکی دو واحدی چشمپوشی کن.
کدام پایهی ESP32 برای پتانسیومتر بهتر است؟
پایههای ADC1 یعنی 32، 33، 34، 35، 36 و 39. اینها با روشن بودن وایفای هم کار میکنند. GPIO34 انتخاب محبوبی است چون فقط ورودی است و نقشی در بوت ندارد.