مسیریاب مینیمال GPS برای دوچرخه با برد ESP32
سلام به بچههای خانواده بزرگ آیسیمدار! خیلی وقتها وقتی داریم با دوچرخه تو خیابونها یا مسیرهای جدید رکاب میزنیم، چک کردن مداوم گوشی برای پیدا کردن مسیر، هم کلافهکنندهست و هم خطرناک. گوشیها بزرگن، باتری زیادی مصرف میکنن و وصل کردنشون روی فرمون دوچرخه همیشه استرس افتادنشون رو به همراه داره.
به همین خاطر تو این پروژه قراره یه مسیریاب مینیمال، جمعوجور و فوقالعاده کاربردی به اسم TURN-OFF برای دوچرخهمون بسازیم. این گجت به جای نشون دادن نقشههای شلوغ، فقط جهتهای اصلی و فاصله تا پیچ بعدی رو با گرافیک ساده و جذاب بهت نشون میده تا تمام تمرکزت فقط روی جاده باشه.
مغز متفکر این پروژه یه برد ESP32 هستش که با کمک گوشی موبایلت، دادههای GPS رو میگیره. گوشیت پردازشهای سنگین و پیدا کردن مسیر رو انجام میده و برد ESP32 فقط وظیفه نمایش اطلاعات رو بر عهده داره. با این روش سرعت سیستم حسابی بالا میره و باتری کمتری هم مصرف میشه. پس اگه آمادهای تا دوچرخهت رو هوشمند کنی، بریم سراغ ساخت این پروژه جذاب!
قطعات موردنیاز
- برد ESP32-S3 شرکت Waveshare با نمایشگر TFT ابعاد 1.69 اینچ
- باتری لیتیومپلیمر 1000 میلیآمپرساعت
- کلید کشویی قطع و وصل تغذیه
- فیلامنت Hyper PLA (برای پرینت سهبعدی)
- دو تا پیچ M3.5
- سه تا پیچ M2
- کابل تایپ سی
- سیم جامپر
- هویه و سیم لحیم
- نرمافزار آردوینو (Arduino IDE)
- نرمافزار فیوژن 360 (Fusion 360)
- رابط برنامهنویسی (API) سرویس OpenRouteService
طراحی و پرینت سهبعدی قاب مسیریاب
اول از همه میخواستم یه قاب جمعوجور طراحی کنم تا دستگاه به جای یه برد الکترونیکی لخت که روی فرمون بسته شده، شبیه یه گجت حرفهای و یکپارچه با دوچرخه به نظر برسه. تمرکز اصلیم روی کوچیک و تمیز نگه داشتن ظاهر نهایی بود.
کل بدنه تو نرمافزار Fusion 360 و دقیقا بر اساس ابعاد برد نمایشگردار ESP32 طراحی شده. چون نمایشگر و میکروکنترلر روی یه برد قرار دارن، تونستیم قاب رو خیلی کوچیک نگه داریم. طراحی نهایی از 5 تا قطعه پرینتشده تشکیل شده:
- در بالایی قاب
- بدنه اصلی پایینی
- گیره دوتکه برای اتصال به فرمون
- روکش پلاستیکی کلید پاور
پایه اتصال جوری دو تکه طراحی شده تا بتونی اون رو با پیچ مستقیما دور فرمون دوچرخه محکم کنی. یه طرف این گیره به بدنه اصلی وصله و طرف دیگه روی فرمون قفل میشه. امکانات کاربردی دیگهای هم به طراحی اضافه کردم:
- بریدگی دقیق برای پورت تایپ سی
- شیار کناری برای دسترسی به کلید تغذیه
- پایههای پیچ یکپارچه داخل بدنه
- لبههای برجسته برای محافظت از لبههای نمایشگر
روکش نارنجی کلید رو هم جداگانه رنگ زدم تا هم ظاهر دستگاه یه کم جذابتر بشه و هم موقع دوچرخهسواری راحتتر بتونم دکمه رو پیدا کنم. حالا برای رسیدن به بهترین ابعاد، چند بار نمونههای آزمایشی کوچیک پرینت گرفتم تا اونا رو روی دوچرخه تست کنم. همین تغییرات کوچیک روی نتیجه نهایی تأثیر فوقالعادهای داشت، چیزایی مثل:
- زاویه دید راحت به نمایشگر
- عرض مناسب برای پایه اتصال
- ضخامت کلی بدنه
- محل دقیق قرارگیری کلید
در نهایت تمام قطعات رو با فیلامنت Hyper PLA پرینت کردم. این متریال هم ظاهر خیلی تمیزی به قطعه میده و هم مقاومت کافی برای استفاده مداوم تو محیط بیرون رو داره.
سرهمبندی مدار و قطعات الکترونیکی
بخش الکترونیک این پروژه رو عمدا خیلی ساده و مینیمال نگه داشتیم. چون برد ESP32-S3 شرکت Waveshare خودش میکروکنترلر و نمایشگر رو با هم داره، فقط به چند تا قطعه جانبی برای راهاندازی کار نیاز داشتیم:
- برد نمایشگردار ESP32-S3
- یه باتری لیتیومپلیمری 1000 میلیآمپرساعت
- یه کلید کشویی کوچیک
تو باید باتری رو مستقیما به کانکتور مخصوصش روی برد ESP32 وصل کنی. برای اینکه بتونی دستگاه رو روشن و خاموش کنی، کلید کشویی رو به صورت سری تو مسیر یکی از سیمهای باتری لحیم کن.
بیشترین چالش تو این مرحله فقط این بود که طول سیمها رو جوری تنظیم کنم که بدون گیر کردن یا شلوغکاری، به راحتی داخل قاب کوچیکمون جا بشن.
راستش رو بخوای حین مونتاژ یه اتفاق اعصابخردکن هم افتاد؛ کانکتور باتری روی برد موقع کار کنده شد! به جای اینکه کل برد رو عوض کنم، مجبور شدم با لحیم کردن یه تیکه سیم جامپر کوچیک مستقیما روی برد، اتصال رو دوباره برقرار کنم. از اون لحظههایی بود که با خودم گفتم "خب... انگار طراحی نهایی قراره همین شکلی باشه!"
البته این بدترین بخش ماجرا نبود! موقع لحیمکاری، یه فشار کوچیک به لبه صفحه نمایش وارد کردم و باعث شد یه ترک ریز بخوره. به خاطر همین، یه نوار رنگی گلیچمانند همیشه تو یه سمت صفحه دیده میشه.
چون تا اون لحظه زمان زیادی برای مونتاژ گذاشته بودم، تصمیم گرفتم بیخیال تعویض بشم و این صفحه آسیبدیده رسما بخشی از ظاهر نهایی گجت من شد.
مونتاژ نهایی قطعات داخل بدنه
وقتی پرینت قطعات تموم شد و مدار الکترونیکی رو لحیم کردی، وقتشه که با ظرافت همه چیز رو داخل قاب سر جای خودشون محکم کنی. اول نمایشگر رو از داخل به بخش بالایی قاب پیچ کن تا صفحه نمایش کاملا با سطح رویی مماس بشه. بعد کلید کشویی رو تو شیار مخصوصی که کنار قاب در نظر گرفتیم جا بزن و روکش نارنجی رو روش نصب کن.
حالا باتری رو دقیقا پشت برد اصلی قرار بده. این کار باعث میشه از فضای خالی حداکثر استفاده رو ببری و ضخامت نهایی دستگاه زیاد نشه. گیره اتصال به فرمون رو هم با پیچهای M2 به صفحه پشتی بدنه ببند. این گیره بعد از مونتاژ کامل، وظیفه داره دستگاه رو سفت و محکم روی فرمون دوچرخه نگه داره.
چون ابعاد بدنه رو دقیقا به اندازه خود برد طراحی کرده بودیم، فضای داخلی خیلی کیپ و فشردهست. تمام حواست تو این مرحله باید به مسیر سیمها باشه تا موقع بستن قاب له نشن یا به قطعات دیگه آسیبی نزنن.
بعد از بستن کامل قاب، متوجه میشی که دستگاه چقدر قرص و محکم شده؛ بهخصوص با در نظر گرفتن اینکه تو طول پروسه ساخت بارها قطعات رو تغییر دادیم تا به این طراحی نهایی برسیم.
نصب مسیریاب روی فرمون دوچرخه
حالا که دستگاه کاملا سرهم شده، باید اون رو روی دوچرخه نصب کنیم و ببینیم موقع سواری چقدر کاربردی و راحته. مسیریاب رو دقیقا تو بخش وسطی فرمون با استفاده از همون گیره دوتکه پلاستیکی نصب کن. دو تکه گیره با استفاده از پیچهای M3.5 به هم سفت میشن. بعد از محکم کردن پیچها، میبینی که دستگاه بدون هیچ لرزشی سر جاش فیکس شده.
من عمدا وسط فرمون رو برای نصب انتخاب کردم تا حین رکاب زدن همهچیز طبیعی به نظر برسه. با این زاویه، میتونی اطلاعات مسیریابی رو با یه نگاه سریع بخونی و نیازی نیست چشمهات رو از جاده برداری.
یکی از اهداف اصلیم این بود که این سیستم روی دوچرخه ظاهر زمخت یا بزرگی نداشته باشه. به لطف طراحی فشرده و پایه یکپارچه، نتیجه نهایی بیشتر شبیه یه کامپیوتر هوشمند بازاری شده تا یه برد الکترونیکی که با چسب و بست به فرمون وصل شده!
وقتی برای اولین بار روشنش میکنی و اطلاعات و جهتهای مسیر رو به صورت زنده حین حرکت میبینی، تازه حس میکنی که این پروژه چقدر واقعی و جذابه.
معماری نرمافزاری و نحوه ارتباط برد با گوشی
نمای کلی:
مسیریاب TURN-OFF از یه معماری نرمافزاری توزیعشده استفاده میکنه. یعنی به جای اینکه تمام بار پردازش رو روی دوش میکروکنترلر بندازیم، وظایف رو بین گوشی موبایل و برد ESP32 تقسیم کردیم. با این روش، هر دستگاه کاری رو میکنه که توش مهارت بیشتری داره و سیستم هم سریعتر میشه.
ایده اصلی این بود:
- گوشی موبایل = انجام پردازشهای سنگین
- برد ESP32 = نمایش گرافیکی اطلاعات
این روش باعث شد توسعه نرمافزار و پیدا کردن باگها حین کار خیلی راحتتر بشه.
ساختار سیستم:
نرمافزار ما به دو بخش کاملا مجزا تقسیم میشه. اول، وباپلیکیشن روی گوشی که این کارها رو مدیریت میکنه:
- دسترسی به سنسور GPS
- اتصال به اینترنت
- جستجوی اسم مقصد
- تولید و محاسبه مسیر
- منطق و تصمیمگیری مسیریابی
- ارسال دیتا از طریق پروتکل وبسوکت
این وباپلیکیشن با استفاده از HTML، CSS و جاوا اسکریپت نوشته شده. برای پیدا کردن مقصد، از سرویس نقشه Nominatim و برای تعیین مسیر از API سیستم OpenRouteService کمک گرفتیم. وقتی حرکت شروع میشه، گوشی به طور مداوم دستورات رو با وبسوکت برای برد میفرسته. بخش دوم، کدهای گرافیکی روی ESP32 هستن که تمرکزشون فقط روی این موارده:
- رندر کردن تصاویر روی صفحه
- برقراری ارتباط با وبسوکت
- بهروزرسانی رابط کاربری
- سوئیچ کردن بین وضعیتهای مختلف مسیر
این کدها تو محیط آردوینو و با کمک کتابخونههای گرافیکی Adafruit GFX و ST7789 نوشته شدن. در واقع برد ESP32 هیچ تصمیمی برای مسیر نمیگیره و فقط اطلاعات آماده رو از موبایل دریافت میکنه و نمایش میده. تو قدمهای بعدی، هر کدوم از این بخشها رو دقیقتر بررسی میکنیم.
طراحی رابط کاربری (UI) برای نمایشگر
برای طراحی ظاهر صفحات نمایشگر، از ابزار آنلاین Lopaka استفاده کردم. اول یه پروژه جدید باز کردم و ابعاد 240 در 280 پیکسل رو به همراه کتابخونه گرافیکی Adafruit انتخاب کردم تا کدهای خروجی دقیقا با کدهای برد من همخونی داشته باشن.
از اونجایی که صفحه نمایش ما کوچیکه، هدف اصلی این بود که اطلاعات با یه نگاه سریع و حین حرکت کاملا خوانا باشن. به جای اینکه بخوام نقشههای شلوغ یا متنهای اضافه رو روی صفحه جا بدم، تمرکزم رو گذاشتم روی کشیدن فلشهای جهتنمای بزرگ و اعداد واضح برای فاصلهها.
رابط کاربری از ترکیب رنگهای مشکی، سفید و سبز تشکیل شده تا تو نور روز بیشترین کنتراست رو داشته باشه و با ظاهر کلی بدنه هم ست بشه. کلا شش تا صفحه مجزا برای این حالتها طراحی کردم:
- گردش به چپ
- گردش به راست
- ادامه مسیر مستقیم
- دور برگردون
- در حال شروع مسیریابی
- رسیدن به مقصد
به جای استفاده از آیکونهای تکراری نقشهها، فلشهای گرافیکی خودم رو طراحی کردم و وارد برنامه Lopaka کردم. این جزئیات باعث شد نمایشگر خیلی حرفهایتر بشه و حس یه گجت تخصصی رو منتقل کنه.
بعد از تموم شدن طراحی تمام صفحات، کدهای مربوط به رابط کاربری و تصاویر پیکسلی رو برای هر صفحه جداگانه خروجی گرفتم. هر کدوم از این صفحات تو برنامهنویسی ESP32 به یه فایل هدر (Header) مستقل تبدیل شدن تا کدهای اصلی شلوغ نشن و راحتتر بتونم روشون کار کنم.
سیستم کار برد ESP32 اینجوریه که بر اساس دستوری که از گوشی میگیره، بین این صفحات از پیش طراحی شده سوئیچ میکنه. چون بیشتر گرافیک صفحه ثابته، حین حرکت فقط اعدادی مثل فاصله تا پیچ بعدی یا آیپی دستگاه بهصورت زنده تغییر میکنن. این تکنیک باعث میشه صفحه نمایش بیدلیل رفرش نشه و تغییر گرافیکها خیلی نرم و روون اتفاق بیفته.
برنامهنویسی برد ESP32
منطق مسیریابی و کدهای برد ESP32
تو این سمت از پروژه، وظیفه برد ESP32 فقط مدیریت این بخشهاست:
- رندر کردن گرافیک صفحه
- اتصال به شبکه وایفای
- ارتباط با وبسوکت
- سوئیچ بین صفحات مختلف UI
- دریافت و نمایش زنده اطلاعات مسیریابی
برای نوشتن این سیستم تو نرمافزار آردوینو از این کتابخونهها استفاده کردیم:
- کتابخونه Adafruit GFX
- درایور Adafruit ST7789
- کتابخونه WiFi
- کتابخونه WebSocketsServer
- کتابخونه ArduinoJson
1. راهاندازی نمایشگر
نمایشگر ما از طریق پروتکل SPI و با این پینها به میکروکنترلر وصل میشه:
#define TFT_CS 5
#define TFT_DC 4
#define TFT_RST 8
#define TFT_BL 15
#define TFT_MOSI 7
#define TFT_SCLK 6
با این دستور، آبجکت مربوط به نمایشگر رو میسازیم:
Adafruit_ST7789 tft =
Adafruit_ST7789(
TFT_CS,
TFT_DC,
TFT_RST
);
حالا داخل تابع setup، باید نور پسزمینه رو روشن کنیم و صفحه رو راهاندازی کنیم:
pinMode(TFT_BL, OUTPUT);
digitalWrite(TFT_BL, HIGH);
tft.init(240,280);
tft.setRotation(1);
tft.fillScreen(ST77XX_BLACK);
رابط کاربری ما از این ترکیب رنگها استفاده میکنه:
- پسزمینه مشکی
- متنهای سفید
- المانهای سبز به عنوان رنگ تأکیدی
- فلشهای جهتنمای بزرگ
تا وقتی زیر نور آفتاب رکاب میزنی، همهچیز واضح و خوانا باشه.
2. سیستم مدیریت صفحات UI
به جای اینکه کدهای طولانی گرافیک رو تو یه فایل بریزم، هر وضعیت نمایش رو به یه فایل هدر مجزا منتقل کردم. این کار باعث شد مدیریت برنامهها خیلی راحتتر بشه. ساختار پروژه چیزی شبیه به این شد:
turnoff/
turnoff_ui_architecture.ino
screen_start.h
screen_left.h
screen_right.h
screen_straight.h
screen_uturn.h
screen_arrived.h
common_assets.h
Org_01.h
هر کدوم از این فایلها شامل این بخشها هستن:
- کدهای تولید شده تو نرمافزار Lopaka
- آرایههای مربوط به تصاویر بیتمپ
- توابع مربوط به رسم صفحه روی نمایشگر
به عنوان نمونه، تابع رندر کردن یه صفحه به این شکل نوشته شده:
void drawLEFT(
Adafruit_ST7789 &tft
) {
tft.fillScreen(0x0);
tft.drawRect(
10,
10,
261,
221,
0x7C0
);
tft.drawBitmap(
10,
62,
image_left_bits,
136,
120,
0xFFFF
);
}
حالا برد ESP32 بر اساس دیتایی که از گوشی دریافت میکنه، تصمیم میگیره کدوم صفحه رو نشون بده. مثلا:
if(direction == "LEFT"){
drawLEFT(tft);
}
else if(direction == "RIGHT"){
drawRIGHT(tft);
}
else if(direction == "STRAIGHT"){
drawSTRAIGHT(tft);
}
تصاویر مشترکی مثل آیکون شبکه یا گرافیکهای تزئینی رو هم تو یه فایل مشترک به اسم Common.h قرار دادم:
#include "common_assets.h"
این کار از تکرار کدهای گرافیکی جلوگیری میکنه و ساختار برنامه رو تمیز نگه میداره.
3. ارتباط وایفای
برد ESP32 باید مستقیما به هاتاسپات گوشی موبایلت وصل بشه:
const char* ssid =
"S24fe";
const char* password =
"123456789";
منطق اتصال به شبکه هم اینجوریه:
WiFi.mode(WIFI_STA);
WiFi.disconnect();
delay(1000);
WiFi.begin(
ssid,
password
);
while(
WiFi.status()
!= WL_CONNECTED
){
delay(500);
}
وقتی اتصال برقرار شد، برد آیپی لوکال خودش رو هم روی سریال مانیتور میفرسته و هم روی صفحه نمایش نشون میده تا بتونی ببینیش.
4. ارتباط از طریق وبسوکت
یه سرور وبسوکت رو روی پورت 81 راهاندازی میکنیم:
WebSocketsServer webSocket =
WebSocketsServer(81);
حالا وباپلیکیشن روی گوشی با استفاده از این آیپی به برد وصل میشه و اطلاعات مسیر رو بهصورت بیسیم ارسال میکنه. برد ما باید تو تابع حلقه اصلی (loop) همیشه منتظر دریافت این پیامها باشه:
webSocket.loop();
این کد، قلب ارتباطی سیستم ماست.
5. دریافت دادههای مسیر
گوشی، اطلاعات جهتها رو تو قالب پکتهای JSON ارسال میکنه. یه نمونه از این دیتا:
{
"direction":"LEFT",
"distance":"250m",
"road":"Main Road"
}
پیامهای دریافتی از طریق توابع کالبک وبسوکت مدیریت میشن:
void webSocketEvent(
uint8_t num,
WStype_t type,
uint8_t * payload,
size_t length
)
سپس این پیام جیسون با کمک کتابخونه ArduinoJson باز و خونده میشه:
DynamicJsonDocument doc(256);
deserializeJson(doc,payload);
direction =
doc["direction"].as<String>();
distanceText =
doc["distance"].as<String>();
6. آپدیتهای داینامیک روی تصویر
برای اینکه نمایشگر چشمک نزنه، برنامه ما به جای پاک کردن کل صفحه، فقط بخشهایی که تغییر کردن رو آپدیت میکنه. مثلا متن مربوط به مسافت اینجوری بهروز میشه:
tft.fillRect(
150,
95,
110,
35,
ST77XX_BLACK
);
tft.setCursor(161,107);
tft.print(distanceText);
این ترفند ساده، کیفیت و روونی تصویر روی نمایشگر رو به شدت بالا میبره.
7. روند منطقی سیستم حین کار
وقتی برد روشن میشه، به جای نشون دادن یه مسیر الکی، اول وارد صفحه استارت میشه و همونجا میمونه تا اولین دستور رو از گوشی بگیره. به محض رسیدن اطلاعات مسیر، سیستم به یکی از این صفحات سوئیچ میکنه:
- چپ (LEFT)
- راست (RIGHT)
- مستقیم (STRAIGHT)
- دور برگردون (UTURN)
- رسیدن به مقصد (ARRIVED)
این رفتار باعث میشه دستگاه دقیقا مثل یه گجت واقعی و مستقل عمل کنه، نه فقط یه نمایشگر دمو.
8. عیبیابی و دیباگ
بیشتر زمان من موقع برنامهنویسی صرف حل کردن این مشکلات شد:
- مشکلات ارتباط SPI با نمایشگر
- سیاه موندن صفحه
- خطای اتصال وایفای
- قطع شدن وبسوکت
- خطا تو خوندن فایلهای JSON
برای پیدا کردن باگها، از چاپ لاگها روی سریال مانیتور خیلی استفاده کردم:
Serial.println(
WiFi.localIP()
);
این پیامها بهم کمک کردن تا این موارد رو سریع پیدا کنم:
- وصل نشدن به هاتاسپات
- گرفتن آیپی اشتباه
- قطع شدن ارتباط با گوشی
- دریافت بستههای نامعتبر
کدهای کامل برد ESP32 رو میتونی تو فایلهای ضمیمه پروژه پیدا کنی.
معرفی رابطهای برنامهنویسی (API) استفاده شده
تو این پروژه ما به جای نوشتن الگوریتمهای پیچیده نقشه، از دو تا API کاربردی استفاده کردیم:
- رابط نقشه Nominatim (متعلق به OpenStreetMap)
- رابط برنامهنویسی OpenRouteService
از سرویس Nominatim برای این کارها استفاده میشه:
- جستجوی مقصد با اسم
- پیشنهاد دادن آدرسها
- تبدیل اسم به مختصات جغرافیایی
و سرویس OpenRouteService هم این وظایف رو به دوش میکشه:
- تولید و محاسبه مسیر
- مسیریابی مخصوص دوچرخهسواری
- ارائه دستورات نقطه به نقطه (Turn-by-turn)
1. سرویس جستجوی مکان (Nominatim API)
وباپلیکیشن ما درخواست جستجوی مقصد رو به این آدرس میفرسته:
https://nominatim.openstreetmap.org/search
نمونهای از یه درخواست:
fetch(
`https://nominatim.openstreetmap.org/search?format=json&q=${query}`
)
و پاسخی که سرور برمیگردونه شامل این دادههاست:
- اسم کامل مکان
- عرض جغرافیایی
- طول جغرافیایی
- لیست آدرسهای پیشنهادی مشابه
خوبی این سرویس اینه که نیازی به دریافت کلید (API Key) نداره و کاملا رایگانه.
2. سرویس مسیریابی (OpenRouteService API)
برای محاسبه مسیر دوچرخه و گرفتن دستورات راهنمایی، از این آدرس استفاده میکنیم:
https://api.openrouteservice.org/v2/directions/cycling-regular/geojson
دادههایی که به سرور ارسال میکنیم شامل این موارده:
- مختصات فعلی تو
- مختصات مقصد
- درخواست دستورات مسیریابی
ساختار درخواستی که فرستاده میشه به این شکله:
{
coordinates: [
[start_lon, start_lat],
[end_lon, end_lat]
]
}
و پاسخی که میگیریم اطلاعات کاملی بهمون میده:
- مراحل گامبهگام مسیر
- مسافتها
- دستورات مربوط به گردشها
- دیتاهای هندسی مسیر
3. نحوه دریافت کلید اختصاصی OpenRouteService
- اول وارد این لینک شو:
پنل توسعهدهندگان OpenRouteService
- یه حساب کاربری بساز
- یه کلید API جدید تولید کن
- کلیدی که بهت میده رو کپی کن
- اون رو مستقیما داخل کدهای وباپلیکیشن قرار بده:
const API_KEY = "YOUR_API_KEY";
از این به بعد، وباپلیکیشن موبایل از این کلید برای ارتباط با سرور و گرفتن مسیر استفاده میکنه.
ساخت وباپلیکیشن موبایل برای مسیریابی
وباپلیکیشن روی گوشی، مغز متفکر و پردازشگر اصلی پروژه ماست. این برنامه وظایف مهم زیر رو مدیریت میکنه:
- گرفتن دسترسی سنسور GPS
- جستجوی مقصد
- دریافت مسیر ساخته شده
- پردازش و تجزیه دادههای مسیر
- برقراری ارتباط و ارسال دیتا به ESP32
این اپلیکیشن کاملا با استفاده از زبانهای استاندارد وب ساخته شده:
- زبان HTML
- کدهای CSS
- زبان جاوا اسکریپت (JavaScript)
و بدون نیاز به نصب هیچ نرمافزاری، مستقیما داخل مرورگر گوشی اجرا میشه.
1. اتصال به برد ESP32
اول از همه باید آیپی بردی که روی شبکه ساخته شده رو تو کدهای برنامه تعریف کنیم:
const ESP32_IP = "10.42.177.234";
سپس گوشی از طریق وبسوکت مستقیما بهش وصل میشه:
const ws = new WebSocket(
`ws://${ESP32_IP}:81`
);
رویدادهای مربوط به وصل شدن یا قطع شدن هم به این شکل مدیریت میشن:
ws.onopen = ()=>{
console.log("CONNECTED");
status.innerText = "CONNECTED";
};
ws.onclose = ()=>{
console.log("DISCONNECTED");
status.innerText = "DISCONNECTED";
};
2. عیبیابی خطاهای وبسوکت
برای اینکه بفهمم کجای ارتباط مشکل داره، کدهای مربوط به لاگ خطا رو مستقیم تو فرانتاند برنامه نوشتم:
ws.onerror = (e)=>{
console.log(e);
alert("WebSocket Error");
};
این کار کمک کرد تا خیلی سریع این مشکلات رو پیدا کنم:
- وارد کردن آیپی اشتباه
- قطع شدن هاتاسپات گوشی
- خطا تو پروتکل وبسوکت
- رد شدن درخواست از سمت برد
وضعیت اتصال به صورت زنده و گرافیکی تو خود اپلیکیشن هم نمایش داده میشه.
3. جستجوی مکان مقصد
وقتی مقصدی رو جستجو میکنی، برنامه این درخواست رو برای سرور Nominatim میفرسته:
fetch(
`https://nominatim.openstreetmap.org/search?format=json&q=${query}`
)
نتایجی که برمیگردن به شکل داینامیک تو لیست بهت نشون داده میشن:
data.slice(0,5).forEach(place=>{
const div =
document.createElement("div");
div.innerHTML =
place.display_name;
});
وقتی روی مقصدی کلیک میکنی، مختصاتش تو حافظه برنامه ذخیره میشه تا بعدا برای مسیریابی استفاده بشه.
4. ردیابی لحظهای با GPS
برای خوندن موقعیت تو از سنسور گوشی از این متد استفاده میکنیم:
navigator.geolocation.watchPosition()
حین حرکت، این اطلاعات مرتبا آپدیت میشن:
- عرض جغرافیایی فعلی
- طول جغرافیایی فعلی
- حرکت زنده روی مسیر
نمونه کدی که این دیتا رو میگیره:
navigator.geolocation.watchPosition(
(pos)=>{
const lat =
pos.coords.latitude;
const lon =
pos.coords.longitude;
},
(err)=>{
console.log(err);
}
);
5. تولید مسیر
برای ساخت مسیر، اپلیکیشن ما درخواستی به این آدرس میفرسته:
https://api.openrouteservice.org/v2/directions/cycling-regular/geojson
درخواست ما شامل این پارامترهاست:
- مختصات شروع (مکان فعلی)
- مختصات پایان
- پروفایل مسیر مخصوص دوچرخه
- درخواست دستورات ناوبری
نمونه کدی که این کار رو میکنه:
body: JSON.stringify({
coordinates:[
[start_lon,start_lat],
[end_lon,end_lat]
],
instructions:true,
instructions_format:"text"
})
و دیتایی که از سرور بهمون برمیگرده حاوی این اطلاعاته:
- لیست گامهای مسیر
- مسافت کل
- هندسه مسیر روی نقشه
- دستورات مربوط به هر پیچ
6. استخراج دستورات مسیر
حالا که دیتای مسیر رو گرفتیم، باید این مقادیر رو از داخلش بیرون بکشیم:
- مرحله فعلی
- جهت گردش
- میزان فاصله تا پیچ
دستورات پیچیده سرور به کلمات سادهای ترجمه میشن:
LEFT
RIGHT
STRAIGHT
UTURN
ROUNDABOUT
با استفاده از این منطق:
if(text.includes("left"))
return "LEFT";
if(text.includes("right"))
return "RIGHT";
if(text.includes("roundabout"))
return "ROUNDABOUT";
7. مرتبسازی فواصل
اعداد اعشاری مربوط به فاصلهها با این کدها به اعدادی گرد و خوانا تبدیل میشن تا حین حرکت راحتتر خونده بشن:
function formatDistance(m){
if(m < 1000){
return Math.round(m) + "m";
}
return (m/1000).toFixed(1) + "km";
}
این کار باعث میشه نمایشگر خیلی شلوغ و درهم به نظر نرسه.
8. بازسازی زنده مسیر
به جای اینکه مسیر رو فقط یه بار اول کار محاسبه کنیم، سیستم مدام بر اساس موقعیت جدید تو مسیر رو آپدیت میکنه. تقریبا تو بازههای زمانی مشخص شده:
25000ms
مسیر جدید بر اساس مختصات فعلی GPS تولید میشه. اینم منطق کد:
if(
now - lastRouteUpdate > 25000
){
getRoute(
currentLocation,
destinationCoords
);
}
این ویژگی باعث میشه اگه یه پیچ رو اشتباه رفتی، سیستم سریعا مسیر جدید رو جایگزین کنه.
9. ارسال اطلاعات به ESP32
وقتی پردازشهای بالا تموم شد، دیتای آماده از طریق وبسوکت برای برد نمایشگر ارسال میشه:
ws.send(
`${dir}|${dist}|${road}`
);
نمونه پیامی که به برد مخابره میشه:
LEFT|250m|NAVIGATING
برد ESP32 تو کسری از ثانیه این اطلاعات رو میگیره و رابط کاربری رو تغییر میده.
10. دکمههای تست برنامه
برای اینکه مجبور نباشم برای هر تستی سوار دوچرخه بشم، چند تا دکمه مجازی داخل اپلیکیشن اضافه کردم. این دکمهها وضعیتهای از پیش تعیین شده رو به برد میفرستن:
sendData(
"LEFT",
"120m",
"TEST"
);
این دکمهها برای تست این موارد واقعا عالی بودن:
- گرافیک فلشها
- سرعت رندر صفحه
- پایداری ارتباط وبسوکت
- نحوه آپدیت شدن تصویر
بدون اینکه نیاز باشه هر بار از خونه بیرون برم. سورسکد کامل این برنامه هم تو فایلهای ضمیمه پروژه قرار داده شده.
چالشهای پروژه و رفع باگها
راستش رو بخوای بخش بزرگی از زمان این پروژه فقط صرف سر و کله زدن با باگهای عجیب و غریب شد تا بالاخره سیستم پایدار بشه! وقتی پروژهای با این موارد سر و کار داره:
- مدارهای سختافزاری
- شبکه وایفای
- رابطهای API
- سنسور GPS
- پروتکل وبسوکت
- گرافیک و رندر زنده
کاملا طبیعیه که خیلی چیزها تو طول توسعه از کار بیفتن و خراب بشن.
1. مشکلات کتابخانه نمایشگر
تو شروع کار سعی کردم از کتابخونه معروف TFT_eSPI استفاده کنم، اما صفحه نمایش یا کاملا سیاه میموند یا موقع راهاندازی رفتار عجیبی داشت. بعد از کلی درگیری با تنظیمات SPI و پینها، تصمیم گرفتم از ترکیب این دو تا کتابخونه استفاده کنم:
- کتابخونه Adafruit GFX
- درایور Adafruit ST7789
که اتفاقا برای این نمایشگر خاص بسیار پایدارتر جواب داد.
Adafruit_ST7789 tft =
Adafruit_ST7789(
TFT_CS,
TFT_DC,
TFT_RST
);
با همین تغییر ساده، بیشتر مشکلات بالا نیومدن تصویر حل شد.
2. خطای قطع اتصال در اپلیکیشن
گاهی اوقات برد با موفقیت به هاتاسپات گوشی وصل بود، اما وباپلیکیشن همچنان این پیغام رو میداد:
DISCONNECTED
متوجه شدم که مشکل به خاطر نحوه باز کردن فایل HTML تو گوشیه. اجرای مستقیم فایل باعث مسدود شدن پورت وبسوکت میشد. پس فایل رو از طریق برنامه Acode اجرا کردم و اتصال برقرار شد. کدهای خطایابی که اضافه کرده بودم هم خیلی کمک کرد:
ws.onerror = (e)=>{
console.log(e);
alert("WebSocket Error");
};
3. خطاهای مربوط به GPS
گاهی اوقات مرورگر موبایل دسترسی به GPS رو مسدود میکرد و کل مسیریابی از کار میافتاد. برنامه خطاهایی مربوط به عدم دریافت موقعیت مکانی میداد. برای رفع این مشکل، باید دسترسی به موقعیت مکانی (Location) رو از تنظیمات مرورگر به صورت دستی مجاز میکردم.
بعد از این تنظیمات:
navigator.geolocation.watchPosition()
دوباره بدون مشکل شروع به کار میکرد.
4. آپدیتهای سریع و دیوانهکننده صفحه
تو نسخههای اولیه کدم، دستورات مسیر خیلی بیوقفه و سریع آپدیت میشدن. چون مسیر تقریبا لحظهای آپدیت میشد، تصویر روی صفحه پشت سر هم تغییر میکرد. این قضیه باعث این اتفاقات میشد:
- نمایش ناپایدار جهتها
- تغییرات خیلی سریع و آزاردهنده گرافیک
- پرش اشتباه بین گامهای مسیر
برای اینکه سیستم رو آروم کنم، یه تاخیر زمانی منطقی برای درخواست مجدد مسیر تعریف کردم.
25000ms
این بازه زمانی باعث شد تصویر روی صفحه خیلی نرمتر و پایدارتر آپدیت بشه و حین سواری چشم رو اذیت نکنه.
5. مشکل در بهروزرسانی مکان فعلی
یکی از باگهای بزرگ این بود که سیستم، مسیر رو فقط بر اساس همون نقطه اولی که ایستاده بودم حساب میکرد. وقتی با دوچرخه حرکت میکردم، دستگاه هنوز فکر میکرد من جای قبلی هستم و این مشکلات رو به وجود میآورد:
- آپدیت اشتباه مراحل حرکت
- تاخیر تو اعلام پیچها
- تکرار کردن یه دستور ثابت
برای حل این ماجرا، سیستم رو مجبور کردم به جای یه بار ساخت مسیر، هر چند ثانیه مسیر رو با لوکیشن جدید بازسازی کنه.
setInterval(updateRoute, 25000);
با این منطق جدید، وقتی رکاب میزنی اطلاعات به صورت کاملا زنده و واکنشگرا روی فرمون دوچرخه نمایش داده میشه.
6. شکستن سوکت باتری روی مدار
وسط مونتاژ، کانکتور باتری به خاطر فشار از روی ماژول کنده شد. به جای دور انداختن برد، با یه سیم جامپر کوچیک اتصال رو برقرار کردم. هرچند نقشه اصلی این نبود، اما خوشبختانه خیلی خوب جواب داد.
7. ترک خوردن لبه نمایشگر
و در نهایت دردناکترین قسمت داستان! حین لحیمکاری اتصالات نمایشگر، کمی فشار به لبهاش وارد شد و ترک خورد. این اتفاق باعث شد یه نوار رنگی گلیچ همیشه گوشه تصویر بمونه.
چون صفحه همچنان کار میکرد، تصمیم گرفتم بیخیال تعویض دوباره قطعات بشم. الان دیگه این نوار رنگی تبدیل به امضای شخصی این گجت شده!
تست عملی دستگاه روی دوچرخه
وقتی بالاخره تمام سختافزارها، وبسوکتها، کدهای API و منطق مسیریابی در کنار هم پایدار شدن، گجت رو روی دوچرخه نصب کردم تا تو دنیای واقعی اون رو به چالش بکشم. تو این تست عملی بیشترین تمرکزم روی این موارد بود:
- پایداری سیگنال و ردیابی GPS
- سرعت تولید مجدد مسیر تو حین حرکت
- کیفیت و دوام ارتباط وبسوکت
- خوانایی صفحه زیر نور آفتاب
- محکم بودن گیره اتصال روی فرمون
دستگاه رو تو خیابون با اینترنت هاتاسپات موبایل تست کردم، جایی که گوشی درون جیبم مشغول انجام این کارهای سنگین بود:
- گرفتن لوکیشنهای جدید GPS
- درخواست و ساخت مسیر
- پردازش دستورات مسیریابی
و همزمان برد ESP32 روی فرمون، اطلاعات پردازش شده رو روی صفحه میآورد:
- فلشهای بزرگ جهتنما
- اطلاعات فاصله تا پیچ
- تغییر مسیرهای زنده و لحظهای
چیزی که تو خیابون خیلی مهم بود، خوانایی تصویر حین تکونهای دوچرخه بود. چون این نمایشگر کوچیکه، رابط کاربری باید دقیقا اینطور میبود:
- کاملا مینیمال و خلوت
- دارای کنتراست رنگی بالا
- قابل دیدن با یه نگاه سریع و بدون خیره شدن
سیستم بازسازی زنده مسیر تو این تست شاهکار کرد! چون مسیر حین رکاب زدن مدام بر اساس مکان فعلی آپدیت میشد، حس استفاده از یه اپلیکیشن مسیریاب حرفهای رو میداد، نه یه مسیر ثابتی که فقط یه بار اول کار ساخته شده باشه.
بعد از بستن کامل سیستم روی فرمون، نتیجه کار بیشتر شبیه یه کامپیوتر هوشمند دوچرخه شده بود تا یه کاردستی نمونهسازی شده. دیدن اینکه سیستم در حال حرکت مسیر درست رو پیش چشمت نشون میده، واقعا خستگی کل این پروژه رو از تن بیرون کرد.
نتیجهگیری و ایدههایی برای توسعه پروژه
با اینکه این سیستم در نهایت بسیار بهتر از چیزی که فکر میکردم جواب داد، اما باز هم ایدههای زیادی برای نسخههای بعدی وجود داره. تغییراتی که دلم میخواد تو آینده اضافه کنم شامل این موارده:
- طراحی قاب کاملا ضدآب
- ساخت یه برد چاپی (PCB) اختصاصی
- اضافه کردن نشانگر درصد باتری
- پشتیبانی از نقشهها و مسیرهای آفلاین
- اضافه کردن سیستم راهنمای صوتی
- ترکیب سیستم با ماژول سرعتسنج
- طراحی انیمیشنهای نرمتر برای گرافیک صفحه
- تنظیم خودکار نور نمایشگر نسبت به نور محیط
همچنین تو نظرم هست که طراحی داخلی قاب رو کمی تغییر بدم تا سیمکشیها خلوتتر بشن و مونتاژ قطعات راحتتر بشه. راستش این پروژه تو طول ساخت، خیلی بزرگتر از ایده اولیهام شد. چیزی که فقط قرار بود یه نمایشگر ساده روی دوچرخه باشه، تبدیل شد به یه سیستم کامل که شامل این مهارتها بود:
- برنامهنویسی امبدد میکروکنترلر
- طراحی رابط کاربری (UI)
- کار با رابطهای API وب
- ارتباطات شبکهای بیسیم
- دریافت و پردازش موقعیت مکانی
- مسیریابی در لحظه و داینامیک
تو طول مسیر ساخت این گجت، خیلی چیزها خراب شدن، قطعاتی شکستن و کدهای زیادی از نو نوشته شدن. اما در نهایت مسیریاب TURN-OFF دقیقا همون سیستم کوچیک، جذاب و کاملی شد که دنبالش بودم. امیدوارم از این آموزش اختصاصی تو "آیسیمدار" لذت برده باشی. اگه تو هم این پروژه رو ساختی یا ایدهای برای بهتر شدنش داری، حتما نظرت رو با ما به اشتراک بذار!
فایلهای پیوست
تو میتونی اولین سازنده باشی!
اگه پروژه رو ساختی به اشتراک بزار و اعتبار هدیه بگیر
این آموزش با استانداردهای اختصاصی آیسیمدار بازنویسی و بهینهسازی شده و تمامی حقوق انتشار آن متعلق به این مجموعه است و در صورت کپی یا بازنشر پیگرد قانونی خواهد داشت.








































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