آیرو — خودآموز رباتیک

فصل ۱ از ۱۱

پیشرفت ترم
۰٪

ترم ۲ · سنسورها و دنیای آنالوگ

توابع: کارخانه‌های کوچک کد

فصل ۱پیش‌نمایش رایگان
۲۲ دقیقه مطالعه فصل ۱

در این فصل چه می‌سازی#

به ترم ۲ خوش آمدی — ترم سنسورها و دنیای آنالوگ! در این ترم ربات تو بالاخره «حس» پیدا می‌کند: نور را می‌فهمد، دما را می‌فهمد، فاصله را می‌فهمد. ولی قبل از سنسورها، این فصل مهم‌ترین ابزار برنامه‌نویسی کل ترم را به دستت می‌دهد: تابع. اول با یک بازی، ترم ۱ را گرم می‌کنی؛ بعد یاد می‌گیری کد درهم‌ریخته را به کارخانه‌های کوچک و تمیز تبدیل کنی؛ و آخر فصل با همین کارخانه‌ها یک «ریموت یک‌دکمه‌ای» می‌سازی: هر فشار دکمه، یک نمایش کامل نور و صدا. کل ماجرای فصل در همین یک قاب است:

مقایسهٔ دو دنیا: سمتی بشقاب اسپاگتی از روبان‌های درهم کد که روی پنجرهٔ ویرایشگر ریخته، و سمت دیگر همان روبان‌ها مرتب‌شده در چهار جعبهٔ کارخانه‌ای کوچک با خط‌های تمیز بینشان

آخر این فصل می‌توانی:

  • با def یک تابع تعریف کنی، با پرانتز صدایش بزنی و فرق «تعریف کردن» و «اجرا شدن» را توضیح بدهی.
  • به تابع پارامتر بدهی و تابع blink با سه پارامتر (کدام LED، چند بار، با چه سرعتی) بنویسی.
  • با return از تابع جواب بگیری و بگویی چرا return به‌تنهایی چیزی چاپ نمی‌کند.
  • کد شلوغ را به چند تابع کوچک سازمان بدهی و از ترکیبشان ریموت یک‌دکمه‌ای بسازی.
  • سه خطای معروف دنیای توابع (دونقطهٔ فراموش‌شده، صدا زدن قبل از تعریف، پرانتز جاافتاده) را بشناسی و خودت رفعشان کنی.

قبل از شروع: چک‌لیست پیش‌نیاز#

این فصل پل ورود به ترم ۲ است و همه‌چیزش روی ترم ۱ سوار می‌شود. قبل از ادامه، این‌ها باید سر جایشان باشند — هر کدام لنگ می‌زند، اول برگرد همان فصل:

  • از ترم ۱، فصل ۳ و ۵: مدار LED با مقاومت ۲۲۰ اهم را روی Breadboard می‌بندی و با machine.Pin و time.sleep کنترلش می‌کنی.
  • از ترم ۱، فصل ۶: با متغیر، انواع int/float/str و print با f-string راحتی.
  • از ترم ۱، فصل ۷: حلقه‌های while True و for i in range(...) را می‌نویسی و تورفتگی را رعایت می‌کنی.
  • از ترم ۱، فصل ۸ و ۹: دکمه با pull-up داخلی (فشرده = ۰)، if/else و debounce ساده.
  • از ترم ۱، فصل ۱۰: Buzzer را با PWM در حد tone کنترل می‌کنی: freq() برای زیر و بمی، duty(512) روشن و duty(0) خاموش.

لوازم این فصل — خبر خوب: خرید جدید ندارد؛ همه‌چیز از کیت ترم ۱ است:

قطعه / ابزار تعداد جایگزین
ESP32 DevKit سی‌پین + کابل USB دیتا ۱ — (قلب دوره، جایگزین ندارد)
Breadboard بزرگ ۱ دو Breadboard کوچک کنار هم
LED قرمز / زرد / سبز ۳ سه LED هم‌رنگ هم اشکالی ندارد
مقاومت ۲۲۰ اهم ۳ ۳۳۰ اهم هم قابل قبول است (کمی کم‌نورتر)
دکمهٔ فشاری (Push button) ۱ دو سر یک سیم جامپر را لحظه‌ای به هم بزن
Buzzer پسیو ۱ بدون آن، بخش‌های صدا را فقط بخوان
سیم جامپر حدود ۱۰

۱. امتحان ورودی: سه پیش‌بینی#

قبل از هر چیز جدید، یک بازی — اسمش را گذاشته‌ایم «امتحان ورودی ترم ۲»، ولی نترس: نمره ندارد و به سبک مسابقه است. سه قطعه کد از ترم ۱ می‌بینی. قانون بازی: قبل از خواندن جواب، دستت را روی آن بگذار و بلند بگو این کد چه می‌کند.

صحنهٔ مسابقهٔ بازی‌های قدیمی: سه پنل جواب درخشان کنار هم روی پس‌زمینهٔ سرمه‌ای با خط‌های مدار و ستاره‌های پیکسلی

قطعهٔ اول:

import machine
import time

led = machine.Pin(25, machine.Pin.OUT)

while True:
    led.value(1)
    time.sleep(1)
    led.value(0)
    time.sleep(1)

پیش‌بینی‌ات چیست؟

جواب: LED روی پایهٔ ۲۵ یک ثانیه روشن و یک ثانیه خاموش می‌شود و چون while True هیچ‌وقت تمام نمی‌شود، این چشمک تا ابد ادامه دارد. درست گفتی؟ یعنی فصل‌های ۵ و ۷ ترم ۱ سر جایشان‌اند.

قطعهٔ دوم کمی موذی‌تر است:

import machine

button = machine.Pin(32, machine.Pin.IN, machine.Pin.PULL_UP)
led = machine.Pin(27, machine.Pin.OUT)

while True:
    if button.value() == 0:
        led.value(1)
    else:
        led.value(0)

سؤال: LED سبز کی روشن می‌شود — وقتی دکمه را فشار می‌دهیم یا وقتی رها می‌کنیم؟

جواب: وقتی فشار می‌دهیم! این همان تلهٔ معروف pull-up داخلی است: در حالت عادی پایه عدد ۱ را می‌خواند و فشردن دکمه آن را به ۰ می‌رساند؛ پس شرط == 0 یعنی «دکمه فشرده شده». اگر برعکس گفتی، اشکالی ندارد — ده دقیقه فصل ۸ ترم ۱ را مرور کن.

و قطعهٔ سوم، خاطره‌انگیزترینش:

import machine
import time

buzzer = machine.PWM(machine.Pin(4))
notes = [262, 330, 392, 523]

for n in notes:
    buzzer.freq(n)
    buzzer.duty(512)
    time.sleep(0.3)

buzzer.duty(0)

جواب: جعبهٔ نت‌های فصل ۱۰ ترم ۱: چهار نت پشت سر هم پخش می‌شوند — دو، می، سل، دویِ بالا — و آخرش duty(0) صدا را قطع می‌کند. راستی آن عدد ۵۱۲ را می‌بینی؟ ترم قبل قول دادیم یک روز رازش را بگوییم. آن روز فصل بعد است؛ فعلاً همین‌قدر بدان که ۵۱۲ عدد جادویی نیست — یک راز شیرین دارد.

هر سه را درست گفتی؟ امتحان ورودی را با نمرهٔ کامل پاس شدی. یکی دو تا اشتباه؟ هیچ اتفاق بدی نیفتاده — فقط حالا می‌دانی کدام فصل ترم ۱ را مرور کنی.

و این هم بازیگرهای جدید این ترم — فقط برای آشنایی، چون امروز به هیچ‌کدامشان دست نمی‌زنیم:

شش قطعهٔ کیت ترم ۲ روی میز، هر کدام در کارت خودش: پتانسیومتر چرخان، سنسور نور LDR، ماژول آبی دما و رطوبت DHT11، نمایشگر کوچک OLED، موتور Servo با پره‌های سفید و سنسور فاصلهٔ دوچشمی

یک ولوم چرخان، یک سنسور نور، یک سنسور دما و رطوبت، یک نمایشگر کوچک، یک موتور که زاویه می‌فهمد و یک سنسور فاصله که مثل خفاش کار می‌کند. فهرست کامل کیت ترم ۲ و جایگزین‌های ارزان‌تر در فایل معرفی ترم آمده — با خانواده تصمیم بگیرید.

۲. تابع چیست؟ درد اسپاگتی و ایدهٔ کارخانه#

بگذار از یک درد شروع کنیم. پروژهٔ چراغ راهنمایی ترم ۱ را یادت هست؟ کدش کار می‌کرد، ولی یک مشکل بزرگ داشت: همه‌چیز پشت سر هم ریخته شده بود. چشمک‌زدن LED را سه جای مختلف کپی کرده بودیم؛ بوق زدن را دو جا. به این می‌گویند کد اسپاگتی — مثل بشقاب ماکارونی که نمی‌دانی هر رشته از کجا شروع شده. اگر بخواهی سرعت چشمک را عوض کنی، باید سه جا را پیدا و اصلاح کنی — و احتمالاً یکی را جا می‌اندازی.

راه‌حل چیزی است به اسم function — تابع. تابع مثل یک کارخانهٔ کوچک است: یک بار می‌سازیش، بعد هر وقت خواستی فقط دکمه‌اش را می‌زنی.

دو مثال از زندگی خودت. اول، ماشین آبمیوه‌گیری: یک بار طرز کارش ساخته شده؛ تو فقط میوه می‌ریزی و دکمه را می‌زنی. لازم نیست هر بار از اول موتور و تیغه اختراع کنی. دوم، مخصوص گیمرها: دکمهٔ کمبو. در خیلی از بازی‌ها می‌توانی یک حرکت ترکیبی — پرش، چرخش، ضربه — را یک بار تعریف کنی و روی یک دکمه بگذاری. از آن به بعد، یک دکمه یعنی کل حرکت. تابع دقیقاً همین است: یک بار تعریف می‌کنی، بعد با یک اسم صدایش می‌زنی.

۳. اولین تابع: def و صدا زدن#

Thonny را باز کن و یک فایل جدید بساز — برای این تمرین حتی سخت‌افزار هم لازم نیست. می‌خواهیم بنویسیم def say_hello():. سه چیز در این خط هست:

  • def — مخفف define یعنی «تعریف کن». داریم به پایتون می‌گوییم: می‌خواهم یک کارخانهٔ جدید بسازم.
  • اسم تابع، say_hello — اسم را ما انتخاب می‌کنیم، مثل اسم متغیر: انگلیسی، بدون فاصله.
  • پرانتز باز و بسته و بعد دونقطه. پرانتز فعلاً خالی است — بخش بعد می‌فهمی برای چیست — و دونقطه یعنی «بدنهٔ این تابع از خط بعد شروع می‌شود». درست مثل if و while و for که هر سه دونقطه داشتند. و مثل همان‌ها، بدنهٔ تابع باید تورفتگی داشته باشد.

ولی قبل از نسخهٔ درست، خودت یک خرابکاری عمدی بکن که همهٔ ما حداقل صد بار انجامش می‌دهیم: دونقطه را ننویس و Run بزن:

def say_hello()
    print("Salam! Man yek tabe hastam")
Traceback (most recent call last):
  File "<stdin>", line 1
SyntaxError: invalid syntax

SyntaxError را از ترم ۱ می‌شناسی: «جمله‌ات از نظر دستور زبان غلط است.» پایتون حتی شماره خط را هم گفته. قانون طلایی: هر خطی که با def شروع می‌شود، مثل if و for، حتماً با دونقطه تمام می‌شود. دونقطه را اضافه کن و دوباره Run بزن.

اجرا شد و... هیچ اتفاقی نیفتاد! نه خطا، نه پیغام سلام. چرا؟

این مهم‌ترین نکتهٔ فصل است: تعریف کردنِ تابع با اجرا کردنش فرق دارد. def فقط کارخانه را می‌سازد؛ کارخانه تا وقتی سفارشی نیاید، تولیدی ندارد. مثل ذخیره‌کردن شمارهٔ یک دوست در گوشی: شماره را ذخیره کرده‌ای، ولی تا زنگ نزنی، تماسی برقرار نمی‌شود.

دو گوشی کنار هم: در یکی کارت مخاطب فقط ذخیره می‌شود و گوشی ساکت است، در دیگری همان مخاطب واقعاً در حال تماس گرفته شدن است و گوشی می‌درخشد و زنگ می‌خورد

برای زنگ زدن به تابع، اسمش را با پرانتز می‌نویسیم: say_hello(). به این کار می‌گوییم call — صدا زدن. برنامهٔ کامل این تمرین (فایل src/01_first_function.py کنار درس):

# Term 2 - Session 01 - avalin tabe: tarif va seda zadan
def say_hello():
    # in khat-ha faghat vaghti ejra mishavand ke tabe seda zadeh shavad
    print("Salam! Man yek tabe hastam")
    print("Har bar sedam bezani hamin kar ra mikonam")

# tarif kardan yani sakhtan-e dokme, seda zadan yani feshordan-e an
say_hello()
say_hello()
say_hello()

چک کن: بعد از Run باید هر دو پیغام، سه بار پشت سر هم در Shell چاپ شوند. سه بار صدا زدی، سه بار کل بدنه اجرا شد — یک بار نوشتی، هر چند بار خواستی استفاده کردی. این قدرت تابع است.

دو خرابکاری عمدی دیگر هم مانده — هر دو را خودت انجام بده. اول: خط say_hello() را ببر بالای فایل، قبل از def، و Run بزن:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
NameError: name 'say_hello' isn't defined

«اسمی به نام say_hello تعریف نشده.» چرا؟ چون پایتون فایل را مثل کتاب از بالا به پایین می‌خواند: در خط اول به say_hello() می‌رسد در حالی که هنوز کارخانه‌ای ساخته نشده. قانون: اول تعریف، بعد صدا زدن. برای همین عادت خوب این است که همهٔ تابع‌ها را بالای فایل بنویسیم و برنامهٔ اصلی را پایین. خط را برگردان پایین.

دوم — بی‌سروصداترین خطای امروز: پرانتز را جا بینداز و فقط بنویس say_hello. Run بزن... هیچ خطایی نیست، ولی هیچ سلامی هم چاپ نشد! این از آن باگ‌های موذی است، چون پایتون اصلاً شکایتی نمی‌کند. این‌طور یادت بماند: اسم تابع بدون پرانتز، اسم است — مثل اسم یک نفر روی کاغذ. پرانتز، فعل است — یعنی «انجام بده!». پرانتز که برگردد، تازه صدا زدن اتفاق می‌افتد.

🔧 اگر کار نکرد: بعد از def say_hello(): خطای IndentationError: expected an indented block گرفتی؟ بدنهٔ تابع تورفتگی ندارد — خط‌های بدنه را با چهار فاصله تو ببر؛ در Thonny کلید Tab همین کار را می‌کند. برنامه بدون خطا اجرا می‌شود ولی هیچ اتفاقی نمی‌افتد؟ پایین فایل را چک کن: تابع حتماً باید با پرانتز صدا زده شده باشد.

یک خبر خوب هم برای وقتی دور از میز کارت هستی:

چالش: فایلی که فقط def دارد

اول خودت فکر کن یا امتحان کن — بعد اینجا را باز کن.

۴. پارامتر: جای خالی کارخانه#

حالا آن پرانتز خالی را پر کنیم. فرض کن می‌خواهی LED قرمز گاهی سه بار آرام چشمک بزند، گاهی ده بار تند. با چیزی که تا الان بلدیم باید دو تابع جدا بنویسیم — و اگر فردا چشمک پنج‌تایی خواستیم، تابع سوم؟ نه. راه بهتری هست: پارامتر — جای خالی در تعریف تابع؛ مثل دستگاه آبمیوه‌گیری که «میوه» را تو انتخاب می‌کنی. دستگاه یکی است، ورودی هر بار فرق می‌کند.

اول مدار فصل را ببند — همان نقشهٔ همیشگی دوره:

  1. کابل USB را جدا کن (قانون ترم ۱: سیم جابه‌جا می‌کنیم، برق قطع).
  2. سه LED را با مقاومت‌های ۲۲۰ اهم به پایه‌های ۲۵ (قرمز)، ۲۶ (زرد) و ۲۷ (سبز) وصل کن — پایهٔ بلند سمت مقاومت، پایهٔ کوتاه به GND.
  3. دکمه را روی شکاف وسط سوار کن: یک پایه به پایهٔ ۳۲ و پایهٔ آن‌طرفش به GND.
  4. Buzzer را به پایهٔ ۴ و پایهٔ دیگرش را به GND بزن.
  5. کابل USB را وصل کن.

جدول کامل اتصالات:

Component ESP32 Pin Notes
LED red (+220 ohm) GPIO 25 long leg -> resistor -> GPIO, short -> GND
LED yellow (+220 ohm) GPIO 26 same as above
LED green (+220 ohm) GPIO 27 same as above
Push button GPIO 32 other leg -> GND (internal PULL_UP)
Passive buzzer GPIO 4 other leg -> GND

قبل از تابع، دو میان‌بر کوچک که از امروز استاندارد کدهای این دوره می‌شوند:

  • شکل کوتاه import: تا حالا می‌نوشتیم import machine و بعد هر بار machine.Pin. از این ترم می‌نویسیم from machine import Pin یعنی «از جعبه‌ابزار machine فقط Pin را بردار و بیاور سر میز». از این به بعد به‌جای machine.Pin(25, machine.Pin.OUT) می‌نویسیم Pin(25, Pin.OUT) — همان کار، کدِ خلوت‌تر.
  • time.sleep_ms: برادر دقیق‌ترِ time.sleep که به‌جای ثانیه، میلی‌ثانیه می‌گیرد: time.sleep_ms(500) یعنی نیم ثانیه. برای چشمک‌های تند، شمردن با میلی‌ثانیه خیلی راحت‌تر از نوشتن ۰٫۵ و ۰٫۱ است.

و حالا تابع blink — برنامهٔ کامل (فایل src/02_blink_function.py کنار درس):

# Term 2 - Session 01 - blink ba parameter (LED 25)
from machine import Pin
import time

led_red = Pin(25, Pin.OUT)

def blink(pin_obj, times, delay_ms):
    # pin_obj: kodam LED, times: chand bar, delay_ms: sorat-e cheshmak
    for i in range(times):
        pin_obj.value(1)
        time.sleep_ms(delay_ms)
        pin_obj.value(0)
        time.sleep_ms(delay_ms)

blink(led_red, 3, 500)    # 3 bar cheshmak-e aram
time.sleep_ms(1000)
blink(led_red, 10, 100)   # 10 bar cheshmak-e tond

داخل پرانتزِ تعریف، سه پارامتر گذاشتیم و با ویرگول جدا کردیم: pin_obj یعنی کدام LED — بله، می‌شود خودِ شیء Pin را به تابع داد! — times یعنی چند بار، و delay_ms یعنی هر روشن یا خاموش چند میلی‌ثانیه طول بکشد. داخل بدنه چیز جدیدی نیست: همان حلقهٔ for ترم ۱، فقط به‌جای عددهای ثابت، پارامتر نشسته.

و یک واژهٔ جدید: به مقدارهایی که موقع صدا زدن می‌فرستیم — مثل led_red و 3 و 500 — می‌گویند argument، آرگومان. پارامتر جای خالی است؛ آرگومان چیزی است که در آن جای خالی می‌گذاریم.

چک کن: بعد از Run باید سه چشمک آرام ببینی، یک ثانیه مکث، بعد ده چشمک تند. یک تابع، بی‌نهایت رفتار.

یک آزمایش هم بکن: عمداً یک آرگومان کم بده — blink(led_red, 3) — و Run بزن:

Traceback (most recent call last):
  File "<stdin>", line 12, in <module>
TypeError: function takes 3 positional arguments but 2 were given

پایتون شمرده و شفاف می‌گوید: این تابع سه جای خالی دارد، تو فقط دو تا پر کردی. کارخانهٔ سه‌ورودی با دو ورودی روشن نمی‌شود. آرگومان سوم را برگردان تا درست شود.

۵. return: کارخانه‌ای که محصول تحویل می‌دهد#

تا این‌جا تابع‌های ما فقط «کاری انجام می‌دادند»: چشمک، بوق. ولی بعضی کارخانه‌ها محصول تحویل می‌دهند — مثل ماشین‌حساب: دو عدد می‌دهی، جواب پس می‌گیری. در پایتون به این «پس دادن جواب» می‌گویند return.

بهترین مثالش اتفاقاً به همین ترم ربط دارد: تبدیل دما. سنسور دمایی که چند فصل دیگر وصل می‌کنیم، دما را سلسیوس می‌دهد؛ اگر بخواهیم به فارنهایت — واحد رایج بعضی کشورها — تبدیلش کنیم، فرمولش ضرب در ۱٫۸ و جمع با ۳۲ است. تابعش فقط دو خط است: def c_to_f(c): و بعد return c * 1.8 + 32. کلمهٔ return یعنی: «این مقدار را به هر کسی که مرا صدا زد، تحویل بده.»

حالا یک آزمایش مهم. در فایل بنویس c_to_f(25) — بدون هیچ print — و Run بزن... هیچ‌چیز چاپ نشد! تعجب کردی؟ تابع کار خودش را کرد: عدد ۷۷ را حساب کرد و تحویل داد — ولی ما تحویلش نگرفتیم. جواب مثل بستهٔ پستی برگشت خورد، چون کسی در را باز نکرد. دو راه برای تحویل گرفتن داریم: در متغیر بگذاریم — f = c_to_f(25) — یا مستقیم چاپش کنیم — print(c_to_f(25)). برنامهٔ کامل با همهٔ آزمایش‌ها (فایل src/03_return_c_to_f.py کنار درس):

# Term 2 - Session 01 - return: tabe-i ke javab pas midahad

def c_to_f(c):
    # dama-ye Celsius ra be Fahrenheit tabdil mikonad
    return c * 1.8 + 32

def add(a, b):
    # jam-e do adad, faghat baraye tamrin-e return
    return a + b

# return be tanhayi chizi chap nemikonad!
c_to_f(25)   # in khat ejra mishavad vali hich chizi ruye safhe nemi-ayad

# baraye tahvil gereftan-e javab: motghayer ya print
f = c_to_f(25)
print("25 daraje Celsius =", f, "Fahrenheit")
print(f"40 daraje Celsius = {c_to_f(40)} Fahrenheit")
print("2 + 3 =", add(2, 3))

# dar REPL khodet emtehan kon:
# >>> c_to_f(100)
# 212.0
# REPL javab ra az sar-e mehrabani neshan midahad,
# vali dakhel-e barname faghat print ya motghayer!

چک کن: سه خط چاپ‌شده باید ببینی — و هیچ اثری از آن c_to_f(25) تنها.

یک نکتهٔ ریز که بعداً در بخش همیار AI به دردمان می‌خورد: در REPL — همان پنجرهٔ Shell — اگر تایپ کنی c_to_f(100)، خودِ REPL از سر مهربانی جواب را نشانت می‌دهد: ۲۱۲٫۰. ولی این لطفِ REPL است، نه کارِ return. داخل برنامه چنین لطفی وجود ندارد.

چالش: print یا return؟

اول خودت فکر کن یا امتحان کن — بعد اینجا را باز کن.

۶. پروژه: ریموت یک‌دکمه‌ای#

همهٔ قطعات را داری: تابع بدون پارامتر، تابع با پارامتر، return. وقت ساخت است. ایده ساده است: هر بار دکمه را فشار بدهی، یک نمایش کامل اجرا می‌شود — نور مثل موج از روی سه LED رد می‌شود، سه بوق بالارونده می‌زند و آخرش LED سبز جشن می‌گیرد. مثل دکمهٔ کمبو: یک فشار، یک حرکت نمایشی کامل.

زیبایی کار در سازمان‌دهی‌اش است. به‌جای یک کد اسپاگتی بلند، چهار کارخانهٔ کوچک می‌سازیم که هر کدام یک کار را خوب بلدند:

  • blink — که نوشتیم.
  • beep — دو پارامتر freq_hz و duration_ms؛ داخلش همان سه خط آشنای ترم ۱: فرکانس را تنظیم کن، duty(512) صدا روشن، مکث، duty(0) صدا خاموش.
  • chase — موج نور، که خودش سه بار blink را صدا می‌زند.
  • show — کارگردان، که بقیه را به صف می‌کند.

حلقهٔ اصلی هم فقط چند خط است: اگر دکمه فشرده شد، show() را صدا بزن و یک time.sleep_ms(200) برای debounce — همان ترفند فصل ۹ ترم ۱ که نمی‌گذارد یک فشار، چند فشار حساب شود. برنامهٔ کامل (فایل src/04_one_button_show.py کنار درس):

# Term 2 - Session 01 - remote yek-dokmei: har feshar yek show
from machine import Pin, PWM
import time

led_red = Pin(25, Pin.OUT)
led_yellow = Pin(26, Pin.OUT)
led_green = Pin(27, Pin.OUT)
button1 = Pin(32, Pin.IN, Pin.PULL_UP)
buzzer = PWM(Pin(4))
buzzer.duty(0)   # aval-e kar seda ra ghat kon!

def blink(pin_obj, times, delay_ms):
    # cheshmak: kodam LED, chand bar, ba che sorati
    for i in range(times):
        pin_obj.value(1)
        time.sleep_ms(delay_ms)
        pin_obj.value(0)
        time.sleep_ms(delay_ms)

def beep(freq_hz, duration_ms):
    # yek boogh-e kutah ba farkans-e delkhah
    buzzer.freq(freq_hz)
    buzzer.duty(512)    # seda roshan
    time.sleep_ms(duration_ms)
    buzzer.duty(0)      # seda khamush

def chase(delay_ms):
    # mowj-e noor: ghermez -> zard -> sabz
    blink(led_red, 1, delay_ms)
    blink(led_yellow, 1, delay_ms)
    blink(led_green, 1, delay_ms)

def show():
    # kargardan-e nomayesh: hame chiz ra be saf mikonad
    chase(150)
    chase(150)
    beep(600, 150)
    beep(800, 150)
    beep(1000, 300)
    blink(led_green, 3, 100)

print("Dokme ra feshar bede ta show shoroo shavad!")

while True:
    if button1.value() == 0:     # pull-up: feshorde = 0
        show()
        time.sleep_ms(200)       # debounce-e sade az term 1

چک کن: دکمه را فشار بده — موج نور، بوق‌های بالارونده، جشن سبز. دوباره فشار بده — دوباره از اول. حالا لطف تابع را حس می‌کنی؟ اگر بخواهی نمایش را عوض کنی، فقط بدنهٔ show را دست می‌زنی؛ حلقهٔ اصلی هیچ تغییری لازم ندارد. اسم این «سازمان‌دهی کد» است — و از امروز امضای کدنویسی ماست: تابع‌ها بالا، برنامهٔ اصلی کوتاه و خوانا پایین.

🔧 اگر کار نکرد: دکمه را می‌زنی و هیچ اتفاقی نمی‌افتد — یا نمایش خودش مدام اجرا می‌شود؟ پایهٔ دوم دکمه به GND وصل نیست، دکمه روی شکاف وسط درست ننشسته، یا Pin.PULL_UP در کد جا افتاده؛ خط ساخت دکمه را با کد بالا مقایسه کن. Buzzer از همان لحظهٔ اجرا صدای ممتد می‌دهد؟ خط buzzer.duty(0) بعد از ساختن PWM(Pin(4)) جا افتاده — بلافاصله بعد از ساختن شیء PWM صدا را قطع کن. و خطای NameError: name 'blink' isn't defined یعنی یا تابع را پایین‌تر از جای صدا زدن تعریف کرده‌ای، یا اسمش را غلط تایپ کرده‌ای (blnik!) — املای صدا زدن را با خط def مقایسه کن.

و اگر هنوز قطعه‌هایت کامل نیست:

چالش: کارخانه‌ای که کارخانه صدا می‌زند

اول خودت فکر کن یا امتحان کن — بعد اینجا را باز کن.

۷. با AI: توضیح خط‌به‌خط و راستی‌آزمایی#

ترم قبل یاد گرفتی متن خطا را به chatbot بدهی و بپرسی «این error یعنی چه؟». این ترم مهارت جدیدی تمرین می‌کنیم: از AI بخواهیم کد را خط‌به‌خط توضیح بدهد و برایمان مثال تمرینی بسازد.

مثلاً تابع c_to_f را در chatbot می‌چسبانی و می‌نویسی: «من تازه‌کارم. این کد را خط‌به‌خط توضیح بده: هر خط چه کار می‌کند؟» جواب خوبی می‌گیری: توضیح می‌دهد def تابع می‌سازد، c پارامتر ورودی است و خط دوم فرمول تبدیل را حساب می‌کند. بعد درخواست دوم: «یک تمرین شبیه همین برای من بساز که فقط از def و return و عملگرهای حسابی استفاده کند.» و مثلاً پیشنهاد می‌دهد: تابعی بنویس که قد را از سانتی‌متر به اینچ تبدیل کند — تمرینی دقیقاً هم‌سطح خودت.

ولی حواست جمع باشد. در همین گفت‌وگو ممکن است AI وسط توضیحش یک جملهٔ مشکوک بگوید — مثلاً: «وقتی return اجرا شود، نتیجه روی صفحه چاپ می‌شود.» هوم! ما همین چند صفحه پیش خلافش را با چشم دیدیم. چه می‌کنیم؟ آزمایش: فایل جدید، فقط تعریف c_to_f و یک خط c_to_f(25) بدون print. اجرا... Shell خالی است. پس AI این‌جا دقیق نبود: return تحویل می‌دهد، چاپ نمی‌کند؛ چاپ کار print است. با یک آزمایش بیست‌ثانیه‌ای حرف AI را راستی‌آزمایی کردی. AI یک همیار فوق‌العاده است — ولی همیار توست، نه معلم معصوم. داور نهایی همیشه اجرای واقعی کد است.

سه کارت گرافیکی کنار هم: نوجوانی که اول خودش فکر می‌کند و لامپ ایده بالای سرش روشن است، ذره‌بینی که خط‌به‌خط روی کد حرکت می‌کند، و چهرهٔ ربات مهربانی با آچار هشدار کوچک — ربات‌ها هم اشتباه می‌کنند

🤖 با AI: خودت امتحان کن: کد blink را در chatbot بچسبان و بخواه خط‌به‌خطش را توضیح بدهد؛ بعد ازش یک تمرین هم‌سطح بخواه و حلش کن. سه قانون همیشگی سر جایشان‌اند: یک — «اول خودت، بعد AI»: اول حدس خودت را بزن. دو — «خط به خط»: کدی که نمی‌فهمی در پروژه‌ات نگذار. سه — «AI هم اشتباه می‌کند»: جوابش را با اجرا امتحان کن. و مثل همیشه: حساب کاربری فقط با اجازه و کمک والدین.

واژه‌های تازه این فصل#

کلمه تلفظ به حروف فارسی یعنی چه
function فانکشِن تابع؛ کارخانهٔ کوچک کد که یک بار تعریف می‌شود و بارها استفاده می‌شود
define دیفاین تعریف کردن؛ ساختن تابع با کلمهٔ def (هنوز اجرا نیست!)
call کال صدا زدن تابع با پرانتز تا بدنه‌اش واقعاً اجرا شود
parameter پَرَمیتِر جای خالی در تعریف تابع؛ مثل times در blink
argument آرگیومِنت مقدار واقعی که موقع صدا زدن در جای خالی می‌گذاریم؛ مثل عدد ۳
return ریتِرن تحویل دادن جواب تابع به صدازننده (بدون چاپ کردن!)

تمرین‌ها

اول خودت فکر کن یا امتحان کن — بعد اینجا را باز کن.

در فصل بعد#

بالاخره راز عدد ۵۱۲ باز می‌شود — و LED ها یاد می‌گیرند نفس بکشند: کم‌نور، پرنور و هر چیزِ بینشان.

به آخر این فصل رسیدی!

اگر ساختی و جواب داد، این دکمه مال توست.